@nextrope/xrpl
Version:
A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser
169 lines (155 loc) • 5.2 kB
text/typescript
import { ValidationError } from '../../errors'
import { Amount } from '../common'
import { hasFlag } from '../utils'
import {
BaseTransaction,
GlobalFlagsInterface,
validateBaseTransaction,
isAmount,
validateOptionalField,
isDomainID,
} from './common'
/**
* Transaction Flags for an OfferCreate Transaction.
*
* @category Transaction Flags
*/
export enum OfferCreateFlags {
/**
* If enabled, the offer does not consume offers that exactly match it, and
* instead becomes an Offer object in the ledger. It still consumes offers
* that cross it.
*/
tfPassive = 0x00010000,
/**
* Treat the offer as an Immediate or Cancel order. If enabled, the offer
* never becomes a ledger object: it only tries to match existing offers in
* the ledger. If the offer cannot match any offers immediately, it executes
* "successfully" without trading any currency. In this case, the transaction
* has the result code tesSUCCESS, but creates no Offer objects in the ledger.
*/
tfImmediateOrCancel = 0x00020000,
/**
* Treat the offer as a Fill or Kill order . Only try to match existing
* offers in the ledger, and only do so if the entire TakerPays quantity can
* be obtained. If the fix1578 amendment is enabled and the offer cannot be
* executed when placed, the transaction has the result code tecKILLED;
* otherwise, the transaction uses the result code tesSUCCESS even when it was
* killed without trading any currency.
*/
tfFillOrKill = 0x00040000,
/**
* Exchange the entire TakerGets amount, even if it means obtaining more than
* the TakerPays amount in exchange.
*/
tfSell = 0x00080000,
/**
* Indicates the offer is hybrid. (meaning it is part of both a domain and open order book)
* This flag cannot be set if the offer doesn't have a DomainID
*/
tfHybrid = 0x00100000,
}
/**
* Map of flags to boolean values representing {@link OfferCreate} transaction
* flags.
*
* @category Transaction Flags
*
* @example
* ```typescript
* const tx: OfferCreate = {
* Account: 'rhFcpWDHLqpBmX4ezWiA5VLSS4e1BHqhHd',
* TakerGets: '43000.51',
* TakerPays: '12928290425',
* TransactionType: 'OfferCreate',
* Flags: {
* tfPassive: true,
* tfFillOrKill: true,
* },
* }
*
* // Autofill the tx to see how flags actually look compared to the interface usage.
* const autofilledTx = await client.autofill(tx)
* console.log(autofilledTx)
* // {
* // Account: 'rhFcpWDHLqpBmX4ezWiA5VLSS4e1BHqhHd',
* // TakerGets: '43000.51',
* // TakerPays: '12928290425',
* // TransactionType: 'OfferCreate',
* // Flags: 327680,
* // Sequence: 21970384,
* // Fee: '12',
* // LastLedgerSequence: 21970404
* // }
* ```
*/
export interface OfferCreateFlagsInterface extends GlobalFlagsInterface {
tfPassive?: boolean
tfImmediateOrCancel?: boolean
tfFillOrKill?: boolean
tfSell?: boolean
tfHybrid?: boolean
}
/**
* An OfferCreate transaction is effectively a limit order . It defines an
* intent to exchange currencies, and creates an Offer object if not completely.
* Fulfilled when placed. Offers can be partially fulfilled.
*
* @category Transaction Models
*/
export interface OfferCreate extends BaseTransaction {
TransactionType: 'OfferCreate'
Flags?: number | OfferCreateFlagsInterface
/**
* Time after which the offer is no longer active, in seconds since the.
* Ripple Epoch.
*/
Expiration?: number
/** An offer to delete first, specified in the same way as OfferCancel. */
OfferSequence?: number
/** The amount and type of currency being provided by the offer creator. */
TakerGets: Amount
/** The amount and type of currency being requested by the offer creator. */
TakerPays: Amount
/** The domain that the offer must be a part of. */
DomainID?: string
}
/**
* Verify the form and type of an OfferCreate at runtime.
*
* @param tx - An OfferCreate Transaction.
* @throws When the OfferCreate is Malformed.
*/
export function validateOfferCreate(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
if (tx.TakerGets === undefined) {
throw new ValidationError('OfferCreate: missing field TakerGets')
}
if (tx.TakerPays === undefined) {
throw new ValidationError('OfferCreate: missing field TakerPays')
}
if (typeof tx.TakerGets !== 'string' && !isAmount(tx.TakerGets)) {
throw new ValidationError('OfferCreate: invalid TakerGets')
}
if (typeof tx.TakerPays !== 'string' && !isAmount(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')
}
validateOptionalField(tx, 'DomainID', isDomainID, {
txType: 'OfferCreate',
paramName: 'DomainID',
})
if (
tx.DomainID == null &&
hasFlag(tx, OfferCreateFlags.tfHybrid, 'tfHybrid')
) {
throw new ValidationError(
'OfferCreate: tfHybrid flag cannot be set if DomainID is not present',
)
}
}