refactor: Define typescript definition for OfferCreate (#1508)

* refactor: Define OfferCreate transaction type
This commit is contained in:
Nathan Nichols
2021-08-12 09:57:04 -05:00
committed by Mayukha Vadari
parent d438430100
commit 7fde5a2658
7 changed files with 316 additions and 77 deletions

View File

@@ -1,4 +1,4 @@
import { Request } from './../methods'
import { Request } from ".";
export interface BaseRequest {
id: number | string

View File

@@ -48,13 +48,17 @@ const isSigner = (signer: Signer): boolean => {
&& typeof signer.SigningPubKey === 'string'
}
export interface CommonFields {
export interface GlobalFlags {
tfFullyCanonicalSig: boolean,
}
export interface BaseTransaction {
Account: string;
TransactionType: string;
Fee?: string;
Sequence?: number;
AccountTxnID?: string;
Flags?: number;
Flags?: number | GlobalFlags;
LastLedgerSequence?: number;
Memos?: Array<{ Memo: Memo }>;
Signers?: Array<Signer>;
@@ -73,59 +77,59 @@ export interface CommonFields {
* @returns - Void
* @throws - When the common param is malformed.
*/
export function verifyCommonFields(common: CommonFields): void {
export function verifyBaseTransaction(common: BaseTransaction): void {
if (common.Account === undefined)
throw new ValidationError("CommonFields: missing field Account")
throw new ValidationError("BaseTransaction: missing field Account")
if (typeof common.Account !== 'string')
throw new ValidationError("CommonFields: Account not string")
throw new ValidationError("BaseTransaction: Account not string")
if (common.TransactionType === undefined)
throw new ValidationError("CommonFields: missing field TransactionType")
throw new ValidationError("BaseTransaction: missing field TransactionType")
if (typeof common.TransactionType !== 'string')
throw new ValidationError("CommonFields: TransactionType not string")
throw new ValidationError("BaseTransaction: TransactionType not string")
if (!transactionTypes.includes(common.TransactionType))
throw new ValidationError("CommonFields: Unknown TransactionType")
throw new ValidationError("BaseTransaction: Unknown TransactionType")
if (common.Fee !== undefined && typeof common.Fee !== 'string')
throw new ValidationError("CommonFields: invalid Fee")
throw new ValidationError("BaseTransaction: invalid Fee")
if (common.Sequence !== undefined && typeof common.Sequence !== 'number')
throw new ValidationError("CommonFields: invalid Sequence")
throw new ValidationError("BaseTransaction: invalid Sequence")
if (common.Flags !== undefined && typeof common.Flags !== 'number')
throw new ValidationError("CommonFields: invalid Flags")
throw new ValidationError("BaseTransaction: invalid Flags")
if (common.AccountTxnID !== undefined
&& typeof common.AccountTxnID !== 'string')
throw new ValidationError("CommonFields: invalid AccountTxnID")
throw new ValidationError("BaseTransaction: invalid AccountTxnID")
if (common.LastLedgerSequence !== undefined
&& typeof common.LastLedgerSequence !== 'number')
throw new ValidationError("CommonFields: invalid LastLedgerSequence")
throw new ValidationError("BaseTransaction: invalid LastLedgerSequence")
if (common.Memos !== undefined
&& (common.Memos.length === 0 || !common.Memos.every(isMemo)))
throw new ValidationError("CommonFields: invalid Memos")
throw new ValidationError("BaseTransaction: invalid Memos")
if (common.Signers !== undefined
&& (common.Signers.length === 0 || !common.Signers.every(isSigner)))
throw new ValidationError("CommonFields: invalid Signers")
throw new ValidationError("BaseTransaction: invalid Signers")
if (common.SourceTag !== undefined && typeof common.SourceTag !== 'number')
throw new ValidationError("CommonFields: invalid SourceTag")
throw new ValidationError("BaseTransaction: invalid SourceTag")
if (common.SigningPubKey !== undefined
&& typeof common.SigningPubKey !== 'string')
throw new ValidationError("CommonFields: invalid SigningPubKey")
throw new ValidationError("BaseTransaction: invalid SigningPubKey")
if (common.TicketSequence !== undefined
&& typeof common.TicketSequence !== 'number')
throw new ValidationError("CommonFields: invalid TicketSequence")
throw new ValidationError("BaseTransaction: invalid TicketSequence")
if (common.TxnSignature !== undefined
&& typeof common.TxnSignature !== 'string')
throw new ValidationError("CommonFields: invalid TxnSignature")
throw new ValidationError("BaseTransaction: invalid TxnSignature")
}

View File

@@ -1,28 +1,2 @@
import Metadata from "../common/metadata";
export type Transaction = never // (For now)
// AccountSet
// | AccountDelete
// | CheckCancel
// | CheckCash
// | CheckCreate
// | DepositPreauth
// | EscrowCancel
// | EscrowCreate
// | EscrowFinish
// | OfferCancel
// | OfferCreate
// | PaymentTransaction
// | PaymentChannelClaim
// | PaymentChannelCreate
// | PaymentChannelFund
// | SetRegularKey
// | SignerListSet
// | TicketCreate
// | TrustSet
export interface TransactionAndMetadata {
transaction: Transaction;
metadata: Metadata
}
export * from './transaction'
export * from './offerCreate'

View File

@@ -0,0 +1,55 @@
import { ValidationError } from "../../common/errors";
import { Amount, IssuedCurrencyAmount } from "../common";
import { BaseTransaction, GlobalFlags, verifyBaseTransaction } from "./common";
export interface OfferCreateFlags extends GlobalFlags {
tfPassive?: boolean;
tfImmediateOrCancel?: boolean;
tfFillOrKill?: boolean;
tfSell?: boolean;
}
export interface OfferCreate extends BaseTransaction {
TransactionType: "OfferCreate";
Flags?: number | OfferCreateFlags
Expiration?: number;
OfferSequence?: number;
TakerGets: Amount;
TakerPays: Amount;
}
/**
* Verify the form and type of an OfferCreate at runtime.
*
* @param tx - An OfferCreate Transaction
* @returns - Void.
* @throws - When the OfferCreate is Malformed.
*/
export function verifyOfferCreate(tx: OfferCreate): void {
verifyBaseTransaction(tx)
if (tx.TakerGets === undefined)
throw new ValidationError("OfferCreate: missing field TakerGets")
if (tx.TakerPays === undefined)
throw new ValidationError("OfferCreate: missing field TakerPays")
const isIssuedCurrency = (obj: IssuedCurrencyAmount): boolean => {
return Object.keys(obj).length === 3
&& typeof obj.value === 'string'
&& typeof obj.issuer === 'string'
&& typeof obj.currency === 'string'
}
if (typeof tx.TakerGets !== 'string' && !isIssuedCurrency(tx.TakerGets))
throw new ValidationError("OfferCreate: invalid TakerGets")
if (typeof tx.TakerPays !== 'string' && !isIssuedCurrency(tx.TakerPays))
throw new ValidationError("OfferCreate: invalid TakerPays")
if (tx.Expiration !== undefined && typeof tx.Expiration !== 'number')
throw new ValidationError("OfferCreate: invalid Expiration")
if (tx.OfferSequence !== undefined && typeof tx.OfferSequence !== 'number')
throw new ValidationError("OfferCreate: invalid OfferSequence")
}

View File

@@ -0,0 +1,29 @@
import Metadata from "../common/metadata";
import { OfferCreate } from "./offerCreate";
export type Transaction =
// AccountSet
// | AccountDelete
// | CheckCancel
// | CheckCash
// | CheckCreate
// | DepositPreauth
// | EscrowCancel
// | EscrowCreate
// | EscrowFinish
// | OfferCancel
OfferCreate
// | PaymentTransaction
// | PaymentChannelClaim
// | PaymentChannelCreate
// | PaymentChannelFund
// | SetRegularKey
// | SignerListSet
// | TicketCreate
// | TrustSet
export interface TransactionAndMetadata {
transaction: Transaction;
metadata: Metadata
}

View File

@@ -1,5 +1,5 @@
import { ValidationError } from 'ripple-api/common/errors'
import { verifyCommonFields } from '../../src/models/transactions/common'
import { verifyBaseTransaction } from './../../src/models/transactions/common'
import { assert } from 'chai'
/**
@@ -8,7 +8,7 @@ import { assert } from 'chai'
* Providing runtime verification testing for each specific transaction type
*/
describe('Transaction Verification', function () {
it(`Verifies all optional CommonFields`, () => {
it(`Verifies all optional BaseTransaction`, () => {
const txJson = {
Account: "r97KeayHuEsDwyU1yPBVtMLLoQr79QcRFe",
TransactionType: "Payment",
@@ -49,16 +49,16 @@ describe('Transaction Verification', function () {
TxnSignature: "3045022100C6708538AE5A697895937C758E99A595B57A16393F370F11B8D4C032E80B532002207776A8E85BB9FAF460A92113B9C60F170CD964196B1F084E0DAB65BAEC368B66"
}
assert.doesNotThrow(() => verifyCommonFields(txJson))
assert.doesNotThrow(() => verifyBaseTransaction(txJson))
})
it(`Verifies only required CommonFields`, () => {
it(`Verifies only required BaseTransaction`, () => {
const txJson = {
Account: "r97KeayHuEsDwyU1yPBVtMLLoQr79QcRFe",
TransactionType: "Payment",
}
assert.doesNotThrow(() => verifyCommonFields(txJson))
assert.doesNotThrow(() => verifyBaseTransaction(txJson))
})
it (`Handles invalid Fee`, () => {
@@ -69,9 +69,9 @@ describe('Transaction Verification', function () {
} as any
assert.throws(
() => verifyCommonFields(invalidFee),
() => verifyBaseTransaction(invalidFee),
ValidationError,
"CommonFields: invalid Fee"
"BaseTransaction: invalid Fee"
)
})
@@ -83,9 +83,9 @@ describe('Transaction Verification', function () {
} as any
assert.throws(
() => verifyCommonFields(invalidSeq),
() => verifyBaseTransaction(invalidSeq),
ValidationError,
"CommonFields: invalid Sequence"
"BaseTransaction: invalid Sequence"
)
})
@@ -97,9 +97,9 @@ describe('Transaction Verification', function () {
} as any
assert.throws(
() => verifyCommonFields(invalidID),
() => verifyBaseTransaction(invalidID),
ValidationError,
"CommonFields: invalid AccountTxnID"
"BaseTransaction: invalid AccountTxnID"
)
})
@@ -112,9 +112,9 @@ describe('Transaction Verification', function () {
assert.throws(
() => verifyCommonFields(invalidFlags),
() => verifyBaseTransaction(invalidFlags),
ValidationError,
"CommonFields: invalid Flags"
"BaseTransaction: invalid Flags"
)
})
@@ -126,9 +126,9 @@ describe('Transaction Verification', function () {
} as any
assert.throws(
() => verifyCommonFields(invalidLastLedgerSequence),
() => verifyBaseTransaction(invalidLastLedgerSequence),
ValidationError,
"CommonFields: invalid LastLedgerSequence"
"BaseTransaction: invalid LastLedgerSequence"
)
})
@@ -141,9 +141,9 @@ describe('Transaction Verification', function () {
assert.throws(
() => verifyCommonFields(invalidSourceTag),
() => verifyBaseTransaction(invalidSourceTag),
ValidationError,
"CommonFields: invalid SourceTag"
"BaseTransaction: invalid SourceTag"
)
})
@@ -155,9 +155,9 @@ describe('Transaction Verification', function () {
} as any
assert.throws(
() => verifyCommonFields(invalidSigningPubKey),
() => verifyBaseTransaction(invalidSigningPubKey),
ValidationError,
"CommonFields: invalid SigningPubKey"
"BaseTransaction: invalid SigningPubKey"
)
})
@@ -169,9 +169,9 @@ describe('Transaction Verification', function () {
} as any
assert.throws(
() => verifyCommonFields(invalidTicketSequence),
() => verifyBaseTransaction(invalidTicketSequence),
ValidationError,
"CommonFields: invalid TicketSequence"
"BaseTransaction: invalid TicketSequence"
)
})
@@ -183,9 +183,9 @@ describe('Transaction Verification', function () {
} as any
assert.throws(
() => verifyCommonFields(invalidTxnSignature),
() => verifyBaseTransaction(invalidTxnSignature),
ValidationError,
"CommonFields: invalid TxnSignature"
"BaseTransaction: invalid TxnSignature"
)
})
@@ -197,9 +197,9 @@ describe('Transaction Verification', function () {
} as any
assert.throws(
() => verifyCommonFields(invalidSigners),
() => verifyBaseTransaction(invalidSigners),
ValidationError,
"CommonFields: invalid Signers"
"BaseTransaction: invalid Signers"
)
const invalidSigners2 = {
@@ -213,9 +213,9 @@ describe('Transaction Verification', function () {
} as any
assert.throws(
() => verifyCommonFields(invalidSigners2),
() => verifyBaseTransaction(invalidSigners2),
ValidationError,
"CommonFields: invalid Signers"
"BaseTransaction: invalid Signers"
)
})
@@ -232,9 +232,9 @@ describe('Transaction Verification', function () {
} as any
assert.throws(
() => verifyCommonFields(invalidMemo),
() => verifyBaseTransaction(invalidMemo),
ValidationError,
"CommonFields: invalid Memos"
"BaseTransaction: invalid Memos"
)
})
})

177
test/models/offerCreate.ts Normal file
View File

@@ -0,0 +1,177 @@
import { ValidationError } from 'ripple-api/common/errors'
import { verifyOfferCreate } from './../../src/models/transactions/offerCreate'
import { assert } from 'chai'
/**
* OfferCreate Transaction Verification Testing
*
* Providing runtime verification testing for each specific transaction type
*/
describe('OfferCreate Transaction Verification', function () {
it (`verifies valid OfferCreate`, () => {
const offer = {
Account: "r3rhWeE31Jt5sWmi4QiGLMZnY3ENgqw96W",
Fee: "10",
Flags: 0,
LastLedgerSequence: 65453019,
Sequence: 40949322,
SigningPubKey: "03C48299E57F5AE7C2BE1391B581D313F1967EA2301628C07AC412092FDC15BA22",
Expiration: 10,
OfferSequence: 12,
TakerGets: {
currency: "DSH",
issuer: "rcXY84C4g14iFp6taFXjjQGVeHqSCh9RX",
value: "43.11584856965009"
},
TakerPays: "12928290425",
TransactionType: "OfferCreate",
TxnSignature: "3045022100D874CDDD6BB24ED66E83B1D3574D3ECAC753A78F26DB7EBA89EAB8E7D72B95F802207C8CCD6CEA64E4AE2014E59EE9654E02CA8F03FE7FCE0539E958EAE182234D91",
} as any
assert.doesNotThrow(() => verifyOfferCreate(offer))
const offer2 = {
Account: "r3rhWeE31Jt5sWmi4QiGLMZnY3ENgqw96W",
Fee: "10",
Flags: 0,
LastLedgerSequence: 65453019,
Sequence: 40949322,
SigningPubKey: "03C48299E57F5AE7C2BE1391B581D313F1967EA2301628C07AC412092FDC15BA22",
TakerGets: "12928290425",
TakerPays: {
currency: "DSH",
issuer: "rcXY84C4g14iFp6taFXjjQGVeHqSCh9RX",
value: "43.11584856965009"
},
TransactionType: "OfferCreate",
TxnSignature: "3045022100D874CDDD6BB24ED66E83B1D3574D3ECAC753A78F26DB7EBA89EAB8E7D72B95F802207C8CCD6CEA64E4AE2014E59EE9654E02CA8F03FE7FCE0539E958EAE182234D91",
} as any
assert.doesNotThrow(() => verifyOfferCreate(offer2))
const offer3 = {
Account: "r3rhWeE31Jt5sWmi4QiGLMZnY3ENgqw96W",
Fee: "10",
Flags: 0,
LastLedgerSequence: 65453019,
Sequence: 40949322,
SigningPubKey: "03C48299E57F5AE7C2BE1391B581D313F1967EA2301628C07AC412092FDC15BA22",
TakerGets: {
currency: "DSH",
issuer: "rcXY84C4g14iFp6taFXjjQGVeHqSCh9RX",
value: "43.11584856965009"
},
TakerPays: {
currency: "DSH",
issuer: "rcXY84C4g14iFp6taFXjjQGVeHqSCh9RX",
value: "43.11584856965009"
},
TransactionType: "OfferCreate",
TxnSignature: "3045022100D874CDDD6BB24ED66E83B1D3574D3ECAC753A78F26DB7EBA89EAB8E7D72B95F802207C8CCD6CEA64E4AE2014E59EE9654E02CA8F03FE7FCE0539E958EAE182234D91",
} as any
assert.doesNotThrow(() => verifyOfferCreate(offer3))
})
it (`throws w/ invalid Expiration`, () => {
const offer = {
Account: "r3rhWeE31Jt5sWmi4QiGLMZnY3ENgqw96W",
Fee: "10",
Flags: 0,
LastLedgerSequence: 65453019,
Sequence: 40949322,
SigningPubKey: "03C48299E57F5AE7C2BE1391B581D313F1967EA2301628C07AC412092FDC15BA22",
Expiration: "11",
TakerGets: "12928290425",
TakerPays: {
currency: "DSH",
issuer: "rcXY84C4g14iFp6taFXjjQGVeHqSCh9RX",
value: "43.11584856965009"
},
TransactionType: "OfferCreate",
TxnSignature: "3045022100D874CDDD6BB24ED66E83B1D3574D3ECAC753A78F26DB7EBA89EAB8E7D72B95F802207C8CCD6CEA64E4AE2014E59EE9654E02CA8F03FE7FCE0539E958EAE182234D91",
} as any
assert.throws(
() => verifyOfferCreate(offer),
ValidationError,
"OfferCreate: invalid Expiration"
)
})
it (`throws w/ invalid OfferSequence`, () => {
const offer = {
Account: "r3rhWeE31Jt5sWmi4QiGLMZnY3ENgqw96W",
Fee: "10",
Flags: 0,
LastLedgerSequence: 65453019,
Sequence: 40949322,
SigningPubKey: "03C48299E57F5AE7C2BE1391B581D313F1967EA2301628C07AC412092FDC15BA22",
OfferSequence: "11",
TakerGets: "12928290425",
TakerPays: {
currency: "DSH",
issuer: "rcXY84C4g14iFp6taFXjjQGVeHqSCh9RX",
value: "43.11584856965009"
},
TransactionType: "OfferCreate",
TxnSignature: "3045022100D874CDDD6BB24ED66E83B1D3574D3ECAC753A78F26DB7EBA89EAB8E7D72B95F802207C8CCD6CEA64E4AE2014E59EE9654E02CA8F03FE7FCE0539E958EAE182234D91",
} as any
assert.throws(
() => verifyOfferCreate(offer),
ValidationError,
"OfferCreate: invalid OfferSequence"
)
})
it (`throws w/ invalid TakerPays`, () => {
const offer = {
Account: "r3rhWeE31Jt5sWmi4QiGLMZnY3ENgqw96W",
Fee: "10",
Flags: 0,
LastLedgerSequence: 65453019,
Sequence: 40949322,
SigningPubKey: "03C48299E57F5AE7C2BE1391B581D313F1967EA2301628C07AC412092FDC15BA22",
OfferSequence: "11",
TakerGets: "12928290425",
TakerPays: 10,
TransactionType: "OfferCreate",
TxnSignature: "3045022100D874CDDD6BB24ED66E83B1D3574D3ECAC753A78F26DB7EBA89EAB8E7D72B95F802207C8CCD6CEA64E4AE2014E59EE9654E02CA8F03FE7FCE0539E958EAE182234D91",
} as any
assert.throws(
() => verifyOfferCreate(offer),
ValidationError,
"OfferCreate: invalid TakerPays"
)
})
it (`throws w/ invalid TakerGets`, () => {
const offer = {
Account: "r3rhWeE31Jt5sWmi4QiGLMZnY3ENgqw96W",
Fee: "10",
Flags: 0,
LastLedgerSequence: 65453019,
Sequence: 40949322,
SigningPubKey: "03C48299E57F5AE7C2BE1391B581D313F1967EA2301628C07AC412092FDC15BA22",
OfferSequence: "11",
TakerGets: 11,
TakerPays: {
currency: "DSH",
issuer: "rcXY84C4g14iFp6taFXjjQGVeHqSCh9RX",
value: "43.11584856965009"
},
TransactionType: "OfferCreate",
TxnSignature: "3045022100D874CDDD6BB24ED66E83B1D3574D3ECAC753A78F26DB7EBA89EAB8E7D72B95F802207C8CCD6CEA64E4AE2014E59EE9654E02CA8F03FE7FCE0539E958EAE182234D91",
} as any
assert.throws(
() => verifyOfferCreate(offer),
ValidationError,
"OfferCreate: invalid TakerGets"
)
})
})