Skip to content

Commit

Permalink
Merge branch 'amm' into beta3
Browse files Browse the repository at this point in the history
  • Loading branch information
khancode committed Feb 23, 2023
2 parents 3ba43ec + 5c7e315 commit c5fdcc5
Show file tree
Hide file tree
Showing 16 changed files with 1,074 additions and 33 deletions.
2 changes: 1 addition & 1 deletion packages/ripple-keypairs/test/api.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import assert from 'assert'
import fixtures from './fixtures/api.json'
import * as fixtures from './fixtures/api.json'
import * as api from '../src'

const decodeSeed = api.decodeSeed
Expand Down
1 change: 1 addition & 0 deletions packages/xrpl/HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr
* Improved typescript typing

### Added
- `getNFTokenID` lets you get the NFTokenID after minting an NFT


### Changed
Expand Down
1 change: 0 additions & 1 deletion packages/xrpl/src/client/connection.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/* eslint-disable max-lines -- Connection is a large file w/ lots of imports/exports */

import { EventEmitter } from 'events'
import { Agent } from 'http'

Expand Down
2 changes: 1 addition & 1 deletion packages/xrpl/src/models/methods/accountNFTs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export interface AccountNFTsRequest extends BaseRequest {
*
* @category Responses
*/
interface AccountNFToken {
export interface AccountNFToken {
Flags: number
Issuer: string
NFTokenID: string
Expand Down
6 changes: 3 additions & 3 deletions packages/xrpl/src/models/methods/ammInfo.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Amount, IssuedCurrency, IssuedCurrencyAmount } from '../common'
import { Amount, Currency, IssuedCurrencyAmount } from '../common'

import { BaseRequest, BaseResponse } from './baseMethod'

Expand All @@ -15,13 +15,13 @@ export interface AMMInfoRequest extends BaseRequest {
* Specifies one of the pool assets (XRP or token) of the AMM instance.
* Both asset and asset2 must be defined to specify an AMM instance.
*/
asset?: IssuedCurrency
asset: Currency

/**
* Specifies the other pool asset of the AMM instance.
* Both asset and asset2 must be defined to specify an AMM instance.
*/
asset2?: IssuedCurrency
asset2: Currency
}

interface AuthAccount {
Expand Down
12 changes: 6 additions & 6 deletions packages/xrpl/src/models/transactions/AMMBid.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/* eslint-disable complexity -- required for validateAMMBid */
import { ValidationError } from '../../errors'
import { Amount, IssuedCurrency } from '../common'
import { Amount, Currency } from '../common'

import {
BaseTransaction,
isAmount,
isIssue,
isCurrency,
validateBaseTransaction,
} from './common'

Expand All @@ -29,12 +29,12 @@ export interface AMMBid extends BaseTransaction {
/**
* Specifies one of the pool assets (XRP or token) of the AMM instance.
*/
Asset: IssuedCurrency
Asset: Currency

/**
* Specifies the other pool asset of the AMM instance.
*/
Asset2: IssuedCurrency
Asset2: Currency

/**
* This field represents the minimum price that the bidder wants to pay for the slot.
Expand Down Expand Up @@ -71,15 +71,15 @@ export function validateAMMBid(tx: Record<string, unknown>): void {
throw new ValidationError('AMMBid: missing field Asset')
}

if (!isIssue(tx.Asset)) {
if (!isCurrency(tx.Asset)) {
throw new ValidationError('AMMBid: Asset must be an Issue')
}

if (tx.Asset2 == null) {
throw new ValidationError('AMMBid: missing field Asset2')
}

if (!isIssue(tx.Asset2)) {
if (!isCurrency(tx.Asset2)) {
throw new ValidationError('AMMBid: Asset2 must be an Issue')
}

Expand Down
12 changes: 6 additions & 6 deletions packages/xrpl/src/models/transactions/AMMDeposit.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/* eslint-disable complexity -- required for validateAMMDeposit */
import { ValidationError } from '../../errors'
import { Amount, IssuedCurrency, IssuedCurrencyAmount } from '../common'
import { Amount, Currency, IssuedCurrencyAmount } from '../common'

import {
BaseTransaction,
GlobalFlags,
isAmount,
isIssue,
isCurrency,
isIssuedCurrency,
validateBaseTransaction,
} from './common'
Expand Down Expand Up @@ -49,12 +49,12 @@ export interface AMMDeposit extends BaseTransaction {
/**
* Specifies one of the pool assets (XRP or token) of the AMM instance.
*/
Asset: IssuedCurrency
Asset: Currency

/**
* Specifies the other pool asset of the AMM instance.
*/
Asset2: IssuedCurrency
Asset2: Currency

/**
* Specifies the amount of shares of the AMM instance pools that the trader
Expand Down Expand Up @@ -93,15 +93,15 @@ export function validateAMMDeposit(tx: Record<string, unknown>): void {
throw new ValidationError('AMMDeposit: missing field Asset')
}

if (!isIssue(tx.Asset)) {
if (!isCurrency(tx.Asset)) {
throw new ValidationError('AMMDeposit: Asset must be an Issue')
}

if (tx.Asset2 == null) {
throw new ValidationError('AMMDeposit: missing field Asset2')
}

if (!isIssue(tx.Asset2)) {
if (!isCurrency(tx.Asset2)) {
throw new ValidationError('AMMDeposit: Asset2 must be an Issue')
}

Expand Down
12 changes: 6 additions & 6 deletions packages/xrpl/src/models/transactions/AMMVote.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ValidationError } from '../../errors'
import { IssuedCurrency } from '../common'
import { Currency } from '../common'

import { AMM_MAX_TRADING_FEE } from './AMMCreate'
import { BaseTransaction, isIssue, validateBaseTransaction } from './common'
import { BaseTransaction, isCurrency, validateBaseTransaction } from './common'

/**
* AMMVote is used for submitting a vote for the trading fee of an AMM Instance.
Expand All @@ -16,12 +16,12 @@ export interface AMMVote extends BaseTransaction {
/**
* Specifies one of the pool assets (XRP or token) of the AMM instance.
*/
Asset: IssuedCurrency
Asset: Currency

/**
* Specifies the other pool asset of the AMM instance.
*/
Asset2: IssuedCurrency
Asset2: Currency

/**
* Specifies the fee, in basis point.
Expand All @@ -45,15 +45,15 @@ export function validateAMMVote(tx: Record<string, unknown>): void {
throw new ValidationError('AMMVote: missing field Asset')
}

if (!isIssue(tx.Asset)) {
if (!isCurrency(tx.Asset)) {
throw new ValidationError('AMMVote: Asset must be an Issue')
}

if (tx.Asset2 == null) {
throw new ValidationError('AMMVote: missing field Asset2')
}

if (!isIssue(tx.Asset2)) {
if (!isCurrency(tx.Asset2)) {
throw new ValidationError('AMMVote: Asset2 must be an Issue')
}

Expand Down
12 changes: 6 additions & 6 deletions packages/xrpl/src/models/transactions/AMMWithdraw.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/* eslint-disable complexity -- required for validateAMMWithdraw */
import { ValidationError } from '../../errors'
import { Amount, IssuedCurrency, IssuedCurrencyAmount } from '../common'
import { Amount, Currency, IssuedCurrencyAmount } from '../common'

import {
BaseTransaction,
GlobalFlags,
isAmount,
isIssue,
isCurrency,
isIssuedCurrency,
validateBaseTransaction,
} from './common'
Expand Down Expand Up @@ -54,12 +54,12 @@ export interface AMMWithdraw extends BaseTransaction {
/**
* Specifies one of the pool assets (XRP or token) of the AMM instance.
*/
Asset: IssuedCurrency
Asset: Currency

/**
* Specifies the other pool asset of the AMM instance.
*/
Asset2: IssuedCurrency
Asset2: Currency

/**
* Specifies the amount of shares of the AMM instance pools that the trader
Expand Down Expand Up @@ -99,15 +99,15 @@ export function validateAMMWithdraw(tx: Record<string, unknown>): void {
throw new ValidationError('AMMWithdraw: missing field Asset')
}

if (!isIssue(tx.Asset)) {
if (!isCurrency(tx.Asset)) {
throw new ValidationError('AMMWithdraw: Asset must be an Issue')
}

if (tx.Asset2 == null) {
throw new ValidationError('AMMWithdraw: missing field Asset2')
}

if (!isIssue(tx.Asset2)) {
if (!isCurrency(tx.Asset2)) {
throw new ValidationError('AMMWithdraw: Asset2 must be an Issue')
}

Expand Down
36 changes: 33 additions & 3 deletions packages/xrpl/src/models/transactions/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Amount } from '../common'

interface CreatedNode {
export interface CreatedNode {
CreatedNode: {
LedgerEntryType: string
LedgerIndex: string
NewFields: { [field: string]: unknown }
}
}

interface ModifiedNode {
export interface ModifiedNode {
ModifiedNode: {
LedgerEntryType: string
LedgerIndex: string
Expand All @@ -19,7 +19,7 @@ interface ModifiedNode {
}
}

interface DeletedNode {
export interface DeletedNode {
DeletedNode: {
LedgerEntryType: string
LedgerIndex: string
Expand All @@ -29,6 +29,36 @@ interface DeletedNode {

export type Node = CreatedNode | ModifiedNode | DeletedNode

/**
* A typeguard to check if a node is a CreatedNode.
*
* @param node - A node from metadata.
* @returns whether the given node is a CreatedNode.
*/
export function isCreatedNode(node: Node): node is CreatedNode {
return Object.prototype.hasOwnProperty.call(node, `CreatedNode`)
}

/**
* A typeguard to check if a node is a ModifiedNode.
*
* @param node - A node from metadata.
* @returns whether the given node is a ModifiedNode.
*/
export function isModifiedNode(node: Node): node is ModifiedNode {
return Object.prototype.hasOwnProperty.call(node, `ModifiedNode`)
}

/**
* A typeguard to check if a node is a DeletedNode.
*
* @param node - A node from metadata.
* @returns whether the given node is a DeletedNode.
*/
export function isDeletedNode(node: Node): node is DeletedNode {
return Object.prototype.hasOwnProperty.call(node, `DeletedNode`)
}

export interface TransactionMetadata {
AffectedNodes: Node[]
DeliveredAmount?: Amount
Expand Down
85 changes: 85 additions & 0 deletions packages/xrpl/src/utils/getNFTokenID.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import flatMap from 'lodash/flatMap'

import {
CreatedNode,
isCreatedNode,
isModifiedNode,
ModifiedNode,
TransactionMetadata,
} from '../models/transactions/metadata'

interface NFToken {
NFToken: {
NFTokenID: string
URI: string
}
}

/**
* Gets the NFTokenID for an NFT recently minted with NFTokenMint.
*
* @param meta - Metadata from the response to submitting an NFTokenMint transaction.
* @returns The NFTokenID for the minted NFT.
* @throws if meta is not TransactionMetadata.
*/
export default function getNFTokenID(
meta: TransactionMetadata,
): string | undefined {
/* eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Provides a nicer error for js users */
if (meta.AffectedNodes === undefined) {
throw new TypeError(`Unable to parse the parameter given to getNFTokenID.
'meta' must be the metadata from an NFTokenMint transaction. Received ${JSON.stringify(
meta,
)} instead.`)
}

/*
* When a mint results in splitting an existing page,
* it results in a created page and a modified node. Sometimes,
* the created node needs to be linked to a third page, resulting
* in modifying that third page's PreviousPageMin or NextPageMin
* field changing, but no NFTs within that page changing. In this
* case, there will be no previous NFTs and we need to skip.
* However, there will always be NFTs listed in the final fields,
* as rippled outputs all fields in final fields even if they were
* not changed. Thus why we add the additional condition to check
* if the PreviousFields contains NFTokens
*/

const affectedNodes = meta.AffectedNodes.filter((node) => {
if (isCreatedNode(node)) {
return node.CreatedNode.LedgerEntryType === 'NFTokenPage'
}
if (isModifiedNode(node)) {
return (
node.ModifiedNode.LedgerEntryType === 'NFTokenPage' &&
Boolean(node.ModifiedNode.PreviousFields?.NFTokens)
)
}
return false
})
/* eslint-disable @typescript-eslint/consistent-type-assertions -- Necessary for parsing metadata */
const previousTokenIDSet = new Set(
flatMap(affectedNodes, (node) => {
const nftokens = isModifiedNode(node)
? (node.ModifiedNode.PreviousFields?.NFTokens as NFToken[])
: []
return nftokens.map((token) => token.NFToken.NFTokenID)
}).filter((id) => Boolean(id)),
)

/* eslint-disable @typescript-eslint/no-unnecessary-condition -- Cleaner to read */
const finalTokenIDs = flatMap(affectedNodes, (node) =>
(
(((node as ModifiedNode).ModifiedNode?.FinalFields?.NFTokens ??
(node as CreatedNode).CreatedNode?.NewFields?.NFTokens) as NFToken[]) ??
[]
).map((token) => token.NFToken.NFTokenID),
).filter((nftokenID) => Boolean(nftokenID))
/* eslint-enable @typescript-eslint/consistent-type-assertions -- Necessary for parsing metadata */
/* eslint-enable @typescript-eslint/no-unnecessary-condition -- Cleaner to read */

const nftokenID = finalTokenIDs.find((id) => !previousTokenIDSet.has(id))

return nftokenID
}
Loading

0 comments on commit c5fdcc5

Please sign in to comment.