mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-04 21:15:47 +00:00
feat(tickets): first commit, preparePayment and prepareTicket
This commit is contained in:
@@ -64,7 +64,7 @@
|
||||
"doctoc": "doctoc docs/index.md --title '# RippleAPI Reference' --github --maxlevel 2",
|
||||
"docgen": "node --harmony scripts/build_docs.js",
|
||||
"prepublish": "yarn clean && yarn build",
|
||||
"test": "TS_NODE_PROJECT=src/tsconfig.json nyc mocha --exit",
|
||||
"test": "TS_NODE_PROJECT=src/tsconfig.json nyc mocha --exit -g 'creates a ticket successfully with another ticket'",
|
||||
"test:watch": "TS_NODE_PROJECT=src/tsconfig.json mocha --watch --reporter dot",
|
||||
"format": "prettier --write '{src,test}/**/*.ts'",
|
||||
"lint": "eslint 'src/**/*.ts' 'test/*-test.{ts,js}'",
|
||||
|
||||
@@ -43,6 +43,7 @@ import prepareCheckCreate from './transaction/check-create'
|
||||
import prepareCheckCancel from './transaction/check-cancel'
|
||||
import prepareCheckCash from './transaction/check-cash'
|
||||
import prepareSettings from './transaction/settings'
|
||||
import prepareTicket from './transaction/ticket'
|
||||
import sign from './transaction/sign'
|
||||
import combine from './transaction/combine'
|
||||
import submit from './transaction/submit'
|
||||
@@ -395,6 +396,7 @@ class RippleAPI extends EventEmitter {
|
||||
prepareCheckCreate = prepareCheckCreate
|
||||
prepareCheckCash = prepareCheckCash
|
||||
prepareCheckCancel = prepareCheckCancel
|
||||
prepareTicket = prepareTicket
|
||||
prepareSettings = prepareSettings
|
||||
sign = sign
|
||||
combine = combine
|
||||
|
||||
@@ -13,6 +13,7 @@ function loadSchemas() {
|
||||
require('./schemas/objects/hash128.json'),
|
||||
require('./schemas/objects/hash256.json'),
|
||||
require('./schemas/objects/sequence.json'),
|
||||
require('./schemas/objects/ticket-sequence.json'),
|
||||
require('./schemas/objects/signature.json'),
|
||||
require('./schemas/objects/issue.json'),
|
||||
require('./schemas/objects/ledger-version.json'),
|
||||
@@ -115,6 +116,7 @@ function loadSchemas() {
|
||||
require('./schemas/input/prepare-check-create.json'),
|
||||
require('./schemas/input/prepare-check-cash.json'),
|
||||
require('./schemas/input/prepare-check-cancel.json'),
|
||||
require('./schemas/input/prepare-ticket.json'),
|
||||
require('./schemas/input/compute-ledger-hash.json'),
|
||||
require('./schemas/input/sign.json'),
|
||||
require('./schemas/input/submit.json'),
|
||||
|
||||
18
src/common/schemas/input/prepare-ticket.json
Normal file
18
src/common/schemas/input/prepare-ticket.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "prepareTicketParameters",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {
|
||||
"$ref": "address",
|
||||
"description": "The address of the account that is creating the transaction."
|
||||
},
|
||||
"ticketCount": {
|
||||
"type": "number",
|
||||
"description": "The number of tickets to be created."
|
||||
},
|
||||
"instructions": {"$ref": "instructions"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["address", "ticketCount"]
|
||||
}
|
||||
@@ -9,6 +9,10 @@
|
||||
"description": "The initiating account's sequence number for this transaction.",
|
||||
"$ref": "sequence"
|
||||
},
|
||||
"ticketSequence": {
|
||||
"description": "The ticket sequence to be used for this transaction.",
|
||||
"$ref": "ticket-sequence"
|
||||
},
|
||||
"fee": {
|
||||
"description": "An exact fee to pay for the transaction, before multiplying for multi-signed transactions. See [Transaction Fees](#transaction-fees) for more information.",
|
||||
"$ref": "value"
|
||||
@@ -45,6 +49,10 @@
|
||||
{
|
||||
"description": "maxLedgerVersion and maxLedgerVersionOffset are mutually exclusive",
|
||||
"required": ["maxLedgerVersion", "maxLedgerVersionOffset"]
|
||||
},
|
||||
{
|
||||
"description": "sequence and ticketSequence are mutually exclusive",
|
||||
"required": ["sequence", "ticketSequence"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
"link": "account-sequence-number",
|
||||
"description": "An account transaction sequence number",
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
"minimum": 0
|
||||
}
|
||||
|
||||
8
src/common/schemas/objects/ticket-sequence.json
Normal file
8
src/common/schemas/objects/ticket-sequence.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "ticket-sequence",
|
||||
"link": "account-sequence-number",
|
||||
"description": "An account transaction tickt sequence number",
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
}
|
||||
@@ -20,6 +20,10 @@
|
||||
"$ref": "sequence",
|
||||
"description": "The initiating account's sequence number for this transaction."
|
||||
},
|
||||
"ticketSequence": {
|
||||
"$ref": "ticket-sequence",
|
||||
"description": "The initiating account's ticket sequence number for this transaction."
|
||||
},
|
||||
"maxLedgerVersion": {
|
||||
"oneOf": [
|
||||
{"$ref": "ledgerVersion"},
|
||||
@@ -29,8 +33,14 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["fee", "sequence", "maxLedgerVersion"]
|
||||
}
|
||||
"required": ["fee", "maxLedgerVersion"],
|
||||
"anyOf": [
|
||||
{ "required":
|
||||
[ "sequence" ] },
|
||||
{ "required":
|
||||
[ "ticketSequence" ] }
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["txJSON", "instructions"]
|
||||
|
||||
@@ -132,6 +132,11 @@ export const prepareCheckCancel = _.partial(
|
||||
'prepareCheckCancelParameters'
|
||||
)
|
||||
|
||||
export const prepareTicket = _.partial(
|
||||
schemaValidate,
|
||||
'prepareTicketParameters'
|
||||
)
|
||||
|
||||
export const sign = _.partial(schemaValidate, 'signParameters')
|
||||
|
||||
export const combine = _.partial(schemaValidate, 'combineParameters')
|
||||
|
||||
11
src/ledger/parse/ticket-create.ts
Normal file
11
src/ledger/parse/ticket-create.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import * as assert from 'assert'
|
||||
import {removeUndefined} from '../../common'
|
||||
|
||||
function parseTicketCreate(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'TicketCreate')
|
||||
return removeUndefined({
|
||||
ticketCount: tx.TicketCount
|
||||
})
|
||||
}
|
||||
|
||||
export default parseTicketCreate
|
||||
@@ -16,6 +16,7 @@ import parsePayment from './payment'
|
||||
import parsePaymentChannelClaim from './payment-channel-claim'
|
||||
import parsePaymentChannelCreate from './payment-channel-create'
|
||||
import parsePaymentChannelFund from './payment-channel-fund'
|
||||
import parseTicketCreate from './ticket-create'
|
||||
import parseTrustline from './trustline'
|
||||
|
||||
import parseAmendment from './amendment' // pseudo-transaction
|
||||
@@ -41,6 +42,7 @@ function parseTransactionType(type) {
|
||||
PaymentChannelFund: 'paymentChannelFund',
|
||||
SetRegularKey: 'settings',
|
||||
SignerListSet: 'settings',
|
||||
TicketCreate: 'ticketCreate',
|
||||
TrustSet: 'trustline',
|
||||
|
||||
EnableAmendment: 'amendment', // pseudo-transaction
|
||||
@@ -68,6 +70,7 @@ function parseTransaction(tx: any, includeRawTransaction: boolean): any {
|
||||
paymentChannelClaim: parsePaymentChannelClaim,
|
||||
paymentChannelCreate: parsePaymentChannelCreate,
|
||||
paymentChannelFund: parsePaymentChannelFund,
|
||||
ticketCreate: parseTicketCreate,
|
||||
trustline: parseTrustline,
|
||||
|
||||
amendment: parseAmendment, // pseudo-transaction
|
||||
|
||||
@@ -58,7 +58,6 @@ function signWithKeypair(
|
||||
}
|
||||
|
||||
const serialized = binaryCodec.encode(txToSignAndEncode)
|
||||
|
||||
checkTxSerialization(serialized, tx)
|
||||
|
||||
return {
|
||||
|
||||
45
src/transaction/ticket.ts
Normal file
45
src/transaction/ticket.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import * as _ from 'lodash'
|
||||
import * as utils from './utils'
|
||||
import {Prepare, TransactionJSON, Instructions} from './types'
|
||||
import {RippleAPI} from '..'
|
||||
|
||||
const validate = utils.common.validate
|
||||
const ValidationError = utils.common.errors.ValidationError
|
||||
|
||||
export interface Ticket {
|
||||
account: string,
|
||||
sequence: number
|
||||
}
|
||||
|
||||
function createTicketTransaction(
|
||||
account: string,
|
||||
ticketCount: number
|
||||
): TransactionJSON {
|
||||
|
||||
if (!ticketCount || ticketCount === 0) throw new ValidationError('Ticket count must be greater than 0.')
|
||||
|
||||
const txJSON: any = {
|
||||
TransactionType: 'TicketCreate',
|
||||
Account: account,
|
||||
TicketCount: ticketCount
|
||||
}
|
||||
|
||||
return txJSON
|
||||
}
|
||||
|
||||
function prepareTicket(
|
||||
this: RippleAPI,
|
||||
address: string,
|
||||
ticketCount: number,
|
||||
instructions: Instructions = {}
|
||||
): Promise<Prepare> {
|
||||
try {
|
||||
validate.prepareTicket({address, ticketCount, instructions})
|
||||
const txJSON = createTicketTransaction(address, ticketCount)
|
||||
return utils.prepareTransaction(txJSON, this, instructions)
|
||||
} catch (e) {
|
||||
return Promise.reject(e)
|
||||
}
|
||||
}
|
||||
|
||||
export default prepareTicket
|
||||
@@ -19,6 +19,7 @@ export type TransactionJSON = {
|
||||
|
||||
export type Instructions = {
|
||||
sequence?: number
|
||||
ticketSequence?: number
|
||||
fee?: string
|
||||
// @deprecated
|
||||
maxFee?: string
|
||||
@@ -31,7 +32,8 @@ export type Prepare = {
|
||||
txJSON: string
|
||||
instructions: {
|
||||
fee: string
|
||||
sequence: number
|
||||
sequence?: number
|
||||
ticketSequence?: number
|
||||
maxLedgerVersion?: number
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,14 @@ export type ApiMemo = {
|
||||
function formatPrepareResponse(txJSON: any): Prepare {
|
||||
const instructions = {
|
||||
fee: common.dropsToXrp(txJSON.Fee),
|
||||
sequence: txJSON.Sequence,
|
||||
maxLedgerVersion:
|
||||
txJSON.LastLedgerSequence === undefined ? null : txJSON.LastLedgerSequence
|
||||
}
|
||||
if (txJSON.TicketSequence !== undefined) {
|
||||
instructions['ticketSequence'] = txJSON.TicketSequence
|
||||
} else {
|
||||
instructions['sequence'] = txJSON.Sequence
|
||||
}
|
||||
return {
|
||||
txJSON: JSON.stringify(txJSON),
|
||||
instructions
|
||||
@@ -111,13 +115,15 @@ function prepareTransaction(
|
||||
api: RippleAPI,
|
||||
instructions: Instructions
|
||||
): Promise<Prepare> {
|
||||
|
||||
common.validate.instructions(instructions)
|
||||
common.validate.tx_json(txJSON)
|
||||
const disallowedFieldsInTxJSON = [
|
||||
'maxLedgerVersion',
|
||||
'maxLedgerVersionOffset',
|
||||
'fee',
|
||||
'sequence'
|
||||
'sequence',
|
||||
'ticketSequence'
|
||||
]
|
||||
const badFields = disallowedFieldsInTxJSON.filter(field => txJSON[field])
|
||||
if (badFields.length) {
|
||||
@@ -307,6 +313,7 @@ function prepareTransaction(
|
||||
}
|
||||
|
||||
async function prepareSequence(): Promise<void> {
|
||||
|
||||
if (instructions.sequence !== undefined) {
|
||||
if (
|
||||
newTxJSON.Sequence === undefined ||
|
||||
@@ -323,10 +330,18 @@ function prepareTransaction(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (newTxJSON.Sequence !== undefined) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
// Ticket Sequence
|
||||
if (instructions.ticketSequence !== undefined) {
|
||||
newTxJSON.Sequence = 0
|
||||
newTxJSON.TicketSequence = instructions.ticketSequence
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
try {
|
||||
// Consider requesting from the 'current' ledger (instead of 'validated').
|
||||
const response = await api.request('account_info', {
|
||||
|
||||
@@ -489,5 +489,44 @@ export default <TestSuite>{
|
||||
instructionsWithMaxLedgerVersionOffset
|
||||
)
|
||||
assertResultMatch(response, expectedResponse, 'prepare')
|
||||
}
|
||||
},
|
||||
|
||||
// Tickets
|
||||
'preparePayment with ticketSequence': async (api, address) => {
|
||||
const version = await api.getLedgerVersion()
|
||||
const localInstructions = {
|
||||
maxLedgerVersion: version + 100,
|
||||
fee: '0.000012',
|
||||
ticketSequence: 23
|
||||
}
|
||||
const response = await api.preparePayment(
|
||||
address,
|
||||
REQUEST_FIXTURES.allOptions,
|
||||
localInstructions
|
||||
)
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.ticketSequence, 'prepare')
|
||||
},
|
||||
|
||||
'throws when both sequence and ticketSequence are set': async (
|
||||
api,
|
||||
address
|
||||
) => {
|
||||
const version = await api.getLedgerVersion()
|
||||
const localInstructions = {
|
||||
maxLedgerVersion: version + 100,
|
||||
fee: '0.000012',
|
||||
ticketSequence: 23,
|
||||
sequence: 12
|
||||
}
|
||||
return assertRejects(
|
||||
api.preparePayment(
|
||||
address,
|
||||
REQUEST_FIXTURES.allOptions,
|
||||
localInstructions
|
||||
),
|
||||
ValidationError,
|
||||
'instance.instructions is of prohibited type [object Object]'
|
||||
)
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
53
test/api/prepareTicket/index.ts
Normal file
53
test/api/prepareTicket/index.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import {assertResultMatch, TestSuite} from '../../utils'
|
||||
// import responses from '../../fixtures/responses'
|
||||
// import requests from '../../fixtures/requests'
|
||||
// import {ValidationError} from 'ripple-api/common/errors'
|
||||
// import binary from 'ripple-binary-codec'
|
||||
// import assert from 'assert-diff'
|
||||
// import {RippleAPI} from 'ripple-api'
|
||||
|
||||
// const {schemaValidator} = RippleAPI._PRIVATE
|
||||
// const instructionsWithMaxLedgerVersionOffset = {maxLedgerVersionOffset: 100}
|
||||
// const {preparePayment: REQUEST_FIXTURES} = requests
|
||||
// const {preparePayment: RESPONSE_FIXTURES} = responses
|
||||
// const ADDRESS = 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo'
|
||||
|
||||
/**
|
||||
* Every test suite exports their tests in the default object.
|
||||
* - Check out the "TestSuite" type for documentation on the interface.
|
||||
* - Check out "test/api/index.ts" for more information about the test runner.
|
||||
*/
|
||||
export default <TestSuite>{
|
||||
'creates a ticket successfully with a sequence number': async (api, address) => {
|
||||
const expected = {
|
||||
txJSON:
|
||||
'{"TransactionType":"TicketCreate", "TicketCount": 2, "Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Flags":2147483648,"LastLedgerSequence":8819954,"Sequence":23,"Fee":"12"}',
|
||||
instructions: {
|
||||
maxLedgerVersion: 8819954,
|
||||
sequence: 23,
|
||||
fee: '0.000012'
|
||||
}
|
||||
}
|
||||
const response = await api.prepareTicket(address, 2)
|
||||
assertResultMatch(response, expected, 'prepare')
|
||||
},
|
||||
|
||||
'creates a ticket successfully with another ticket': async (api, address) => {
|
||||
const expected = {
|
||||
txJSON:
|
||||
'{"TransactionType":"TicketCreate", "TicketCount": 1, "Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Flags":2147483648,"LastLedgerSequence":8819954,"Sequence": 0,"TicketSequence":23,"Fee":"12"}',
|
||||
instructions: {
|
||||
maxLedgerVersion: 8819954,
|
||||
ticketSequence: 23,
|
||||
fee: '0.000012'
|
||||
}
|
||||
}
|
||||
const instructions = {
|
||||
maxLedgerVersion: 8819954,
|
||||
ticketSequence: 23,
|
||||
fee: '0.000012'
|
||||
}
|
||||
const response = await api.prepareTicket(address, 1, instructions)
|
||||
assertResultMatch(response, expected, 'prepare')
|
||||
}
|
||||
}
|
||||
3
test/fixtures/responses/index.js
vendored
3
test/fixtures/responses/index.js
vendored
@@ -108,7 +108,8 @@ module.exports = {
|
||||
minAmountXRPXRP: require('./prepare-payment-min-amount-xrp-xrp.json'),
|
||||
allOptions: require('./prepare-payment-all-options.json'),
|
||||
noCounterparty: require('./prepare-payment-no-counterparty.json'),
|
||||
minAmount: require('./prepare-payment-min-amount.json')
|
||||
minAmount: require('./prepare-payment-min-amount.json'),
|
||||
ticketSequence: require('./prepare-payment-ticket-sequence.json')
|
||||
},
|
||||
prepareSettings: {
|
||||
regularKey: require('./prepare-settings-regular-key.json'),
|
||||
|
||||
8
test/fixtures/responses/prepare-payment-ticket-sequence.json
vendored
Normal file
8
test/fixtures/responses/prepare-payment-ticket-sequence.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"txJSON": "{\"Flags\":2147811328,\"TransactionType\":\"Payment\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"Amount\":\"10000\",\"InvoiceID\":\"A98FD36C17BE2B8511AD36DC335478E7E89F06262949F36EB88E2D683BBCC50A\",\"SourceTag\":14,\"DestinationTag\":58,\"Memos\":[{\"Memo\":{\"MemoType\":\"74657374\",\"MemoFormat\":\"746578742F706C61696E\",\"MemoData\":\"7465787465642064617461\"}}],\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":0,\"TicketSequence\":23}",
|
||||
"instructions": {
|
||||
"fee": "0.000012",
|
||||
"ticketSequence": 23,
|
||||
"maxLedgerVersion": 8820051
|
||||
}
|
||||
}
|
||||
@@ -348,6 +348,16 @@ describe('integration tests', function() {
|
||||
})
|
||||
})
|
||||
|
||||
it('ticket', function() {
|
||||
return this.api.getLedgerVersion().then(ledgerVersion => {
|
||||
return this.api
|
||||
.prepareTicket(address, 1, instructions)
|
||||
.then(prepared =>
|
||||
testTransaction(this, 'ticket', ledgerVersion, prepared)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('isConnected', function() {
|
||||
assert(this.api.isConnected())
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user