import { ValidationError } from "xrpl-local/common/errors"; // import requests from '../fixtures/requests' import { xrpToDrops, ISOTimeToRippleTime } from "../../src/utils"; import responses from "../fixtures/responses"; import rippled from "../fixtures/rippled"; import { assertRejects, assertResultMatch, TestSuite } from "../testUtils"; const instructionsWithMaxLedgerVersionOffset = { maxLedgerVersionOffset: 100 }; export const config = { // TODO: The mock server right now returns a hard-coded string, no matter // what "Account" value you pass. We'll need it to support more accurate // responses before we can turn these tests on. skipXAddress: true, }; /** * Every test suite exports their tests in the default object. * - Check out the "TestSuite" type for documentation on the interface. * - Check out "test/client/index.ts" for more information about the test runner. */ export default { "auto-fillable fields - does not overwrite Fee in txJSON": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = instructionsWithMaxLedgerVersionOffset; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Fee: "10", }; const response = await client.prepareTransaction(txJSON, localInstructions); const expected = { txJSON: `{"TransactionType":"DepositPreauth","Account":"${address}","Authorize":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Flags":2147483648,"LastLedgerSequence":8820051,"Fee":"10","Sequence":23}`, instructions: { fee: "0.00001", // Notice there are not always 6 digits after the decimal point as trailing zeros are omitted sequence: 23, maxLedgerVersion: 8820051, }, }; return assertResultMatch(response, expected, "prepare"); }, "does not overwrite Fee in Instructions": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, fee: "0.000014", // CAUTION: This `fee` is specified in XRP, not drops. }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", }; const response = await client.prepareTransaction(txJSON, localInstructions); const expected = { txJSON: `{"TransactionType":"DepositPreauth","Account":"${address}","Authorize":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Flags":2147483648,"LastLedgerSequence":8820051,"Fee":"14","Sequence":23}`, instructions: { fee: "0.000014", sequence: 23, maxLedgerVersion: 8820051, }, }; return assertResultMatch(response, expected, "prepare"); }, "rejects Promise if both are set, even when txJSON.Fee matches instructions.fee": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, fee: "0.000016", }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Fee: "16", }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, "`Fee` in txJSON and `fee` in `instructions` cannot both be set" ); }, "rejects Promise if both are set, when txJSON.Fee does not match instructions.fee": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, fee: "0.000018", }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Fee: "20", }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, "`Fee` in txJSON and `fee` in `instructions` cannot both be set" ); }, "rejects Promise when the Fee is capitalized in Instructions": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, Fee: "0.000022", // Intentionally capitalized in this test, but the correct field would be `fee` }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, 'instance additionalProperty "Fee" exists in instance when not allowed' ); }, "rejects Promise when the fee is specified in txJSON": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = instructionsWithMaxLedgerVersionOffset; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", fee: "10", }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, 'txJSON additionalProperty "fee" exists in instance when not allowed' ); }, "does not overwrite Sequence in txJSON": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Sequence: 100, }; const response = await client.prepareTransaction(txJSON, localInstructions); const expected = { txJSON: `{"TransactionType":"DepositPreauth","Account":"${address}","Authorize":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Flags":2147483648,"LastLedgerSequence":8820051,"Fee":"12","Sequence":100}`, instructions: { fee: "0.000012", sequence: 100, maxLedgerVersion: 8820051, }, }; return assertResultMatch(response, expected, "prepare"); }, "does not overwrite Sequence in Instructions": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", sequence: 100, }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", }; const response = await client.prepareTransaction(txJSON, localInstructions); const expected = { txJSON: `{"TransactionType":"DepositPreauth","Account":"${address}","Authorize":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Flags":2147483648,"LastLedgerSequence":8820051,"Fee":"12","Sequence":100}`, instructions: { fee: "0.000012", sequence: 100, maxLedgerVersion: 8820051, }, }; return assertResultMatch(response, expected, "prepare"); }, "does not overwrite Sequence when same sequence is provided in both txJSON and Instructions": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", sequence: 100, }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", }; const response = await client.prepareTransaction( txJSON, localInstructions ); const expected = { txJSON: `{"TransactionType":"DepositPreauth","Account":"${address}","Authorize":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Flags":2147483648,"LastLedgerSequence":8820051,"Fee":"12","Sequence":100}`, instructions: { fee: "0.000012", sequence: 100, maxLedgerVersion: 8820051, }, }; return assertResultMatch(response, expected, "prepare"); }, "rejects Promise when Sequence in txJSON does not match sequence in Instructions": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", sequence: 100, }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Sequence: 101, }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, "`Sequence` in txJSON must match `sequence` in `instructions`" ); }, "rejects Promise when the Sequence is capitalized in Instructions": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", Sequence: 100, // Intentionally capitalized in this test, but the correct field would be `sequence` }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, 'instance additionalProperty "Sequence" exists in instance when not allowed' ); }, // LastLedgerSequence aka maxLedgerVersion/maxLedgerVersionOffset: "does not overwrite LastLedgerSequence in txJSON": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = {}; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Fee: "10", LastLedgerSequence: 8880000, }; const response = await client.prepareTransaction(txJSON, localInstructions); const expected = { txJSON: `{"TransactionType":"DepositPreauth","Account":"${address}","Authorize":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Flags":2147483648,"LastLedgerSequence":8880000,"Fee":"10","Sequence":23}`, instructions: { fee: "0.00001", // Notice there are not always 6 digits after the decimal point as trailing zeros are omitted sequence: 23, maxLedgerVersion: 8880000, }, }; return assertResultMatch(response, expected, "prepare"); }, "does not overwrite maxLedgerVersion in Instructions": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { maxLedgerVersion: 8890000, }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", }; const response = await client.prepareTransaction(txJSON, localInstructions); const expected = { txJSON: `{"TransactionType":"DepositPreauth","Account":"${address}","Authorize":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Flags":2147483648,"LastLedgerSequence":8890000,"Fee":"12","Sequence":23}`, instructions: { fee: "0.000012", sequence: 23, maxLedgerVersion: 8890000, }, }; return assertResultMatch(response, expected, "prepare"); }, "does not overwrite maxLedgerVersionOffset in Instructions": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxLedgerVersionOffset: 124, }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", }; const response = await client.prepareTransaction(txJSON, localInstructions); const expected = { txJSON: `{"TransactionType":"DepositPreauth","Account":"${address}","Authorize":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Flags":2147483648,"LastLedgerSequence":8820075,"Fee":"12","Sequence":23}`, instructions: { fee: "0.000012", sequence: 23, maxLedgerVersion: 8820075, }, }; return assertResultMatch(response, expected, "prepare"); }, "rejects Promise if txJSON.LastLedgerSequence and instructions.maxLedgerVersion both are set": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { maxLedgerVersion: 8900000, }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Fee: "16", LastLedgerSequence: 8900000, }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, "`LastLedgerSequence` in txJSON and `maxLedgerVersion` in `instructions` cannot both be set" ); }, "rejects Promise if txJSON.LastLedgerSequence and instructions.maxLedgerVersionOffset both are set": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxLedgerVersionOffset: 123, }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Fee: "16", LastLedgerSequence: 8900000, }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, "`LastLedgerSequence` in txJSON and `maxLedgerVersionOffset` in `instructions` cannot both be set" ); }, "rejects Promise if instructions.maxLedgerVersion and instructions.maxLedgerVersionOffset both are set": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxLedgerVersion: 8900000, maxLedgerVersionOffset: 123, }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Fee: "16", }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, "instance is of prohibited type [object Object]" ); }, "rejects Promise if txJSON.LastLedgerSequence and instructions.maxLedgerVersion and instructions.maxLedgerVersionOffset all are set": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxLedgerVersion: 8900000, maxLedgerVersionOffset: 123, }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Fee: "16", LastLedgerSequence: 8900000, }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, "instance is of prohibited type [object Object]" ); }, "rejects Promise when the maxLedgerVersion is capitalized in Instructions": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, MaxLedgerVersion: 8900000, // Intentionally capitalized in this test, but the correct field would be `maxLedgerVersion` }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, 'instance additionalProperty "MaxLedgerVersion" exists in instance when not allowed' ); }, "rejects Promise when the maxLedgerVersion is specified in txJSON": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = instructionsWithMaxLedgerVersionOffset; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", maxLedgerVersion: 8900000, }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, 'txJSON additionalProperty "maxLedgerVersion" exists in instance when not allowed' ); }, "rejects Promise when the maxLedgerVersionOffset is specified in txJSON": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = instructionsWithMaxLedgerVersionOffset; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", maxLedgerVersionOffset: 8900000, }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, 'txJSON additionalProperty "maxLedgerVersionOffset" exists in instance when not allowed' ); }, "rejects Promise when the sequence is specified in txJSON": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = instructionsWithMaxLedgerVersionOffset; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", sequence: 8900000, }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, 'txJSON additionalProperty "sequence" exists in instance when not allowed' ); }, // Paths: is not auto-filled by ripple-lib. // Other errors: "rejects Promise when an unrecognized field is in Instructions": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", foo: "bar", }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, 'instance additionalProperty "foo" exists in instance when not allowed' ); }, "rejects Promise when Account is missing": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", }; // Marking as "any" to get around the fact that TS won't allow this. const txJSON: any = { TransactionType: "DepositPreauth", Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, 'instance requires property "Account"' ); }, "rejects Promise when Account is not a string": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", }; // Marking as "any" to get around the fact that TS won't allow this. const txJSON: any = { Account: 1234, TransactionType: "DepositPreauth", Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, "instance.Account is not of a type(s) string,instance.Account is not exactly one from ," ); }, "rejects Promise when Account is invalid": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", }; const txJSON = { Account: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xkXXXX", // Invalid checksum TransactionType: "DepositPreauth", Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, "instance.Account is not exactly one from ," ); }, // 'rejects Promise when Account is valid but non-existent on the ledger': async ( // client // ) => { // const localInstructions = { // ...instructionsWithMaxLedgerVersionOffset, // maxFee: '0.000012' // } // const txJSON = { // Account: 'rogvkYnY8SWjxkJNgU4ZRVfLeRyt5DR9i', // TransactionType: 'DepositPreauth', // Authorize: 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo' // } // await assertRejects( // client.prepareTransaction(txJSON, localInstructions), // RippledError, // 'Account not found.' // ) // }, "rejects Promise when TransactionType is missing": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", }; // Marking as "any" to get around the fact that TS won't allow this. const txJSON: any = { Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, 'instance requires property "TransactionType"' ); }, // Note: This transaction will fail at the `sign` step: // // Error: DepositPreXXXX is not a valid name or ordinal for TransactionType // // at Function.from (ripple-binary-codec/distrib/npm/enums/index.js:43:15) "prepares tx when TransactionType is invalid": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", }; const txJSON = { Account: address, TransactionType: "DepositPreXXXX", Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", }; const response = await client.prepareTransaction(txJSON, localInstructions); const expected = { txJSON: `{"TransactionType":"DepositPreXXXX","Account":"${address}","Authorize":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Flags":2147483648,"LastLedgerSequence":8820051,"Fee":"12","Sequence":23}`, instructions: { fee: "0.000012", sequence: 23, maxLedgerVersion: 8820051, }, }; return assertResultMatch(response, expected, "prepare"); }, "rejects Promise when TransactionType is not a string": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", }; // Marking as "any" to get around the fact that TS won't allow this. const txJSON: any = { Account: address, TransactionType: 1234, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, "instance.TransactionType is not of a type(s) string" ); }, // Note: This transaction will fail at the `submit` step: // // [RippledError(Submit failed, { resultCode: 'temMALFORMED', // resultMessage: 'Malformed transaction.', // engine_result: 'temMALFORMED', // engine_result_code: -299, // engine_result_message: 'Malformed transaction.', // tx_blob: // '120013240000000468400000000000000C732102E1EA8199F570E7F997A7B34EDFDA0A7D8B38173A17450B121A2EB048FDD16CA97446304402201F0EF6A2DE7F96966F7082294D14F3EC1EF59C21E29443E5858A0120079357A302203CDB7FEBDEAAD93FF39CB589B55778CB80DC3979F96F27E828D5E659BEB26B7A8114D51F9A17208CF113AF23B97ECD5FCD314FBAE52E', // tx_json: // { Account: 'rLRt8bmZFBEeM5VMSxZy15k8KKJEs68W6C', // Fee: '12', // Sequence: 4, // SigningPubKey: // '02E1EA8199F570E7F997A7B34EDFDA0A7D8B38173A17450B121A2EB048FDD16CA9', // TransactionType: 'DepositPreauth', // TxnSignature: // '304402201F0EF6A2DE7F96966F7082294D14F3EC1EF59C21E29443E5858A0120079357A302203CDB7FEBDEAAD93FF39CB589B55778CB80DC3979F96F27E828D5E659BEB26B7A', // hash: // 'C181D470684311658852713DA81F8201062535C8DE2FF853F7DD9981BB85312F' } })] "prepares tx when a required field is missing": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", }; const txJSON = { Account: address, TransactionType: "DepositPreauth", // Authorize: 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo' // Normally required, intentionally removed }; const response = await client.prepareTransaction(txJSON, localInstructions); const expected = { txJSON: `{"TransactionType":"DepositPreauth","Account":"${address}","Flags":2147483648,"LastLedgerSequence":8820051,"Fee":"12","Sequence":23}`, instructions: { fee: "0.000012", sequence: 23, maxLedgerVersion: 8820051, }, }; return assertResultMatch(response, expected, "prepare"); }, "DepositPreauth - Authorize": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", }; const expected = { txJSON: `{"TransactionType":"DepositPreauth","Account":"${address}","Authorize":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Flags":2147483648,"LastLedgerSequence":8820051,"Fee":"12","Sequence":23}`, instructions: { fee: "0.000012", sequence: 23, maxLedgerVersion: 8820051, }, }; const response = await client.prepareTransaction(txJSON, localInstructions); return assertResultMatch(response, expected, "prepare"); }, "DepositPreauth - Unauthorize": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Unauthorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", }; const response = await client.prepareTransaction(txJSON, localInstructions); const expected = { txJSON: `{"TransactionType":"DepositPreauth","Account":"${address}","Unauthorize":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Flags":2147483648,"LastLedgerSequence":8820051,"Fee":"12","Sequence":23}`, instructions: { fee: "0.000012", sequence: 23, maxLedgerVersion: 8820051, }, }; return assertResultMatch(response, expected, "prepare"); }, async AccountDelete(client, address, mockRippled) { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "5.0", // 5 XRP fee for AccountDelete }; const txJSON = { TransactionType: "AccountDelete", Account: address, Destination: "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe", }; const response = await client.prepareTransaction(txJSON, localInstructions); const expected = { txJSON: `{"TransactionType":"AccountDelete","Account":"${address}","Destination":"rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe","Flags":2147483648,"LastLedgerSequence":8820051,"Fee":"12","Sequence":23}`, instructions: { fee: "0.000012", sequence: 23, maxLedgerVersion: 8820051, }, }; return assertResultMatch(response, expected, "prepare"); }, // prepareTransaction - Payment "Payment - normal": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", }; const txJSON = { TransactionType: "Payment", Account: address, Destination: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Amount: { currency: "USD", issuer: "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", value: "0.01", }, SendMax: { currency: "USD", issuer: "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", value: "0.01", }, Flags: 0, }; const response = await client.prepareTransaction(txJSON, localInstructions); assertResultMatch(response, responses.preparePayment.normal, "prepare"); }, "min amount xrp": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", }; const txJSON = { TransactionType: "Payment", Account: address, Destination: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", // Max amount to send. Use 100 billion XRP to // ensure that we send the full SendMax amount. Amount: "100000000000000000", SendMax: { currency: "USD", issuer: "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", value: "0.01", }, DeliverMin: "10000", Flags: client.txFlags.Payment.PartialPayment, }; const response = await client.prepareTransaction(txJSON, localInstructions); assertResultMatch( response, responses.preparePayment.minAmountXRP, "prepare" ); }, "min amount xrp2xrp": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const txJSON = { TransactionType: "Payment", Account: address, Destination: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Amount: "10000", Flags: 0, }; const response = await client.prepareTransaction( txJSON, instructionsWithMaxLedgerVersionOffset ); assertResultMatch( response, responses.preparePayment.minAmountXRPXRP, "prepare" ); }, // 'with all options specified': async (client, address) => { // const ledgerResponse = await client.request({ // command: 'ledger', // ledger_index: 'validated' // }) // const version = ledgerResponse.result.ledger_index // const localInstructions = { // maxLedgerVersion: version + 100, // fee: '0.000012' // } // const txJSON = { // TransactionType: 'Payment', // Account: address, // Destination: 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo', // Amount: '10000', // InvoiceID: // 'A98FD36C17BE2B8511AD36DC335478E7E89F06262949F36EB88E2D683BBCC50A', // SourceTag: 14, // DestinationTag: 58, // Memos: [ // { // Memo: { // MemoType: client.convertStringToHex('test'), // MemoFormat: client.convertStringToHex('text/plain'), // MemoData: client.convertStringToHex('texted data') // } // } // ], // Flags: // 0 | // client.txFlags.Payment.NoRippleDirect | // client.txFlags.Payment.LimitQuality // } // const response = await client.prepareTransaction(txJSON, localInstructions) // assertResultMatch(response, responses.preparePayment.allOptions, 'prepare') // }, "fee is capped at default maxFee of 2 XRP (using txJSON.LastLedgerSequence)": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); client._feeCushion = 1000000; const txJSON = { Flags: 2147483648, TransactionType: "Payment", Account: "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59", Destination: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Amount: { value: "0.01", currency: "USD", issuer: "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", }, SendMax: { value: "0.01", currency: "USD", issuer: "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", }, LastLedgerSequence: 8820051, }; const localInstructions = {}; const expectedResponse = { txJSON: '{"Flags":2147483648,"TransactionType":"Payment","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Destination":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Amount":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"SendMax":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"LastLedgerSequence":8820051,"Fee":"2000000","Sequence":23}', instructions: { fee: "2", sequence: 23, maxLedgerVersion: 8820051, }, }; const response = await client.prepareTransaction( txJSON, localInstructions ); assertResultMatch(response, expectedResponse, "prepare"); }, "fee is capped at default maxFee of 2 XRP (using instructions.maxLedgerVersion)": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); client._feeCushion = 1000000; const txJSON = { Flags: 2147483648, TransactionType: "Payment", Account: "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59", Destination: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Amount: { value: "0.01", currency: "USD", issuer: "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", }, SendMax: { value: "0.01", currency: "USD", issuer: "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", }, }; const localInstructions = { maxLedgerVersion: 8820051, }; const expectedResponse = { txJSON: '{"Flags":2147483648,"TransactionType":"Payment","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Destination":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Amount":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"SendMax":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"LastLedgerSequence":8820051,"Fee":"2000000","Sequence":23}', instructions: { fee: "2", sequence: 23, maxLedgerVersion: 8820051, }, }; const response = await client.prepareTransaction( txJSON, localInstructions ); assertResultMatch(response, expectedResponse, "prepare"); }, // prepareTransaction - Payment "fee is capped to custom maxFeeXRP when maxFee exceeds maxFeeXRP": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); client._feeCushion = 1000000; client._maxFeeXRP = "3"; const localInstructions = { maxFee: "4", // We are testing that this does not matter; fee is still capped to maxFeeXRP }; const txJSON = { Flags: 2147483648, TransactionType: "Payment", Account: "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59", Destination: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Amount: { value: "0.01", currency: "USD", issuer: "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", }, SendMax: { value: "0.01", currency: "USD", issuer: "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", }, LastLedgerSequence: 8820051, }; const expectedResponse = { txJSON: '{"Flags":2147483648,"TransactionType":"Payment","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Destination":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Amount":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"SendMax":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"LastLedgerSequence":8820051,"Fee":"3000000","Sequence":23}', instructions: { fee: "3", sequence: 23, maxLedgerVersion: 8820051, }, }; const response = await client.prepareTransaction(txJSON, localInstructions); assertResultMatch(response, expectedResponse, "prepare"); }, // prepareTransaction - Payment "fee is capped to maxFee": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); client._feeCushion = 1000000; client._maxFeeXRP = "5"; const localInstructions = { maxFee: "4", // maxFeeXRP does not matter if maxFee is lower than maxFeeXRP }; const txJSON = { Flags: 2147483648, TransactionType: "Payment", Account: "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59", Destination: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Amount: { value: "0.01", currency: "USD", issuer: "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", }, SendMax: { value: "0.01", currency: "USD", issuer: "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", }, LastLedgerSequence: 8820051, }; const expectedResponse = { txJSON: '{"Flags":2147483648,"TransactionType":"Payment","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Destination":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Amount":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"SendMax":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"LastLedgerSequence":8820051,"Fee":"4000000","Sequence":23}', instructions: { fee: "4", sequence: 23, maxLedgerVersion: 8820051, }, }; const response = await client.prepareTransaction(txJSON, localInstructions); assertResultMatch(response, expectedResponse, "prepare"); }, // 'fee - calculated fee does not use more than 6 decimal places': async ( // client, // address // ) => { // client.connection.request({ // command: 'config', // data: {loadFactor: 5407.96875} // }) // const expectedResponse = { // txJSON: // '{"Flags":2147483648,"TransactionType":"Payment","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Destination":"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo","Amount":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"SendMax":{"value":"0.01","currency":"USD","issuer":"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"},"LastLedgerSequence":8820051,"Fee":"64896","Sequence":23}', // instructions: { // fee: '0.064896', // sequence: 23, // maxLedgerVersion: 8820051 // } // } // const response = await client.preparePayment( // address, // requests.preparePayment.normal, // instructionsWithMaxLedgerVersionOffset // ) // assertResultMatch(response, expectedResponse, 'prepare') // }, "xaddress-issuer": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", }; const txJSON = { TransactionType: "Payment", Account: address, Destination: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Amount: { currency: "USD", issuer: "XVbehP2sFMQAd5orFAy8Lt6vLHGiDhA7VMAnsv9H6WpuB1s", value: "0.01", }, SendMax: { currency: "USD", issuer: "XVbehP2sFMQAd5orFAy8Lt6vLHGiDhA7VMAnsv9H6WpuB1s", value: "0.01", }, Flags: 0, }; const response = await client.prepareTransaction(txJSON, localInstructions); assertResultMatch(response, responses.preparePayment.normal, "prepare"); }, async PaymentChannelCreate(client, address, mockRippled) { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", }; const response = await client.prepareTransaction( { Account: address, TransactionType: "PaymentChannelCreate", Amount: "1000000", // 1 XRP in drops. Use a string-encoded integer. Destination: "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW", SettleDelay: 86400, PublicKey: "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A", // If cancelAfter is used, you must use RippleTime. // You can use `ISOTimeToRippleTime()` to convert to RippleTime. // Other fields are available (but not used in this test), // including `sourceTag` and `destinationTag`. }, localInstructions ); assertResultMatch( response, responses.preparePaymentChannelCreate.normal, "prepare" ); }, "PaymentChannelCreate full": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const txJSON = { Account: address, TransactionType: "PaymentChannelCreate", Amount: xrpToDrops("1"), // or '1000000' Destination: "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW", SettleDelay: 86400, // Ensure this is in upper case if it is not already PublicKey: "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A".toUpperCase(), CancelAfter: ISOTimeToRippleTime("2017-02-17T15:04:57Z"), SourceTag: 11747, DestinationTag: 23480, }; const response = await client.prepareTransaction(txJSON); assertResultMatch( response, responses.preparePaymentChannelCreate.full, "prepare" ); }, async PaymentChannelFund(client, address, mockRippled) { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", }; const txJSON = { Account: address, TransactionType: "PaymentChannelFund", Channel: "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198", Amount: xrpToDrops("1"), // or '1000000' }; const response = await client.prepareTransaction(txJSON, localInstructions); assertResultMatch( response, responses.preparePaymentChannelFund.normal, "prepare" ); }, "PaymentChannelFund full": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const txJSON = { Account: address, TransactionType: "PaymentChannelFund", Channel: "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198", Amount: xrpToDrops("1"), // or '1000000' Expiration: ISOTimeToRippleTime("2017-02-17T15:04:57Z"), }; const response = await client.prepareTransaction(txJSON); assertResultMatch( response, responses.preparePaymentChannelFund.full, "prepare" ); }, async PaymentChannelClaim(client, address, mockRippled) { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", }; const txJSON = { Account: address, TransactionType: "PaymentChannelClaim", Channel: "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198", Flags: 0, }; const response = await client.prepareTransaction(txJSON, localInstructions); assertResultMatch( response, responses.preparePaymentChannelClaim.normal, "prepare" ); }, "PaymentChannelClaim with renew": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", }; const txJSON = { Account: address, TransactionType: "PaymentChannelClaim", Channel: "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198", Balance: xrpToDrops("1"), // or '1000000' Amount: xrpToDrops("1"), // or '1000000' Signature: "30440220718D264EF05CAED7C781FF6DE298DCAC68D002562C9BF3A07C1E721B420C0DAB02203A5A4779EF4D2CCC7BC3EF886676D803A9981B928D3B8ACA483B80ECA3CD7B9B", PublicKey: "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A", Flags: 0, }; txJSON.Flags |= client.txFlags.PaymentChannelClaim.Renew; const response = await client.prepareTransaction(txJSON, localInstructions); assertResultMatch( response, responses.preparePaymentChannelClaim.renew, "prepare" ); }, "PaymentChannelClaim with close": async (client, address, mockRippled) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", }; const txJSON = { Account: address, TransactionType: "PaymentChannelClaim", Channel: "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198", Balance: xrpToDrops("1"), // or 1000000 Amount: xrpToDrops("1"), // or 1000000 Signature: "30440220718D264EF05CAED7C781FF6DE298DCAC68D002562C9BF3A07C1E721B420C0DAB02203A5A4779EF4D2CCC7BC3EF886676D803A9981B928D3B8ACA483B80ECA3CD7B9B", PublicKey: "32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A", Flags: 0, }; txJSON.Flags |= client.txFlags.PaymentChannelClaim.Close; const response = await client.prepareTransaction(txJSON, localInstructions); assertResultMatch( response, responses.preparePaymentChannelClaim.close, "prepare" ); }, "rejects Promise if both sequence and ticketSecuence are set": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ticketSequence: 23, sequence: 23, }; const txJSON = { TransactionType: "DepositPreauth", Account: address, Authorize: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Fee: "16", }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, "instance is of prohibited type [object Object]" ); }, "sets sequence to 0 if a ticketSequence is passed": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", ticketSequence: 23, }; const txJSON = { TransactionType: "Payment", Account: address, Destination: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Amount: { currency: "USD", issuer: "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", value: "0.01", }, SendMax: { currency: "USD", issuer: "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", value: "0.01", }, Flags: 0, }; const response = await client.prepareTransaction(txJSON, localInstructions); assertResultMatch(response, responses.preparePayment.ticket, "prepare"); }, "rejects Promise if a sequence with value 0 is passed": async ( client, address, mockRippled ) => { mockRippled.addResponse( { command: "server_info" }, rippled.server_info.normal ); mockRippled.addResponse({ command: "fee" }, rippled.fee); mockRippled.addResponse( { command: "ledger_current" }, rippled.ledger_current ); mockRippled.addResponse( { command: "account_info" }, rippled.account_info.normal ); const localInstructions = { ...instructionsWithMaxLedgerVersionOffset, maxFee: "0.000012", sequence: 0, }; const txJSON = { TransactionType: "Payment", Account: address, Destination: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", Amount: { currency: "USD", issuer: "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", value: "0.01", }, SendMax: { currency: "USD", issuer: "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", value: "0.01", }, Flags: 0, }; await assertRejects( client.prepareTransaction(txJSON, localInstructions), ValidationError, "`sequence` cannot be 0" ); }, };