Skip to content

Commit

Permalink
feat(xrp): add memos and destinationTag to xrp crafts
Browse files Browse the repository at this point in the history
  • Loading branch information
jprudent committed Feb 3, 2025
1 parent 304b241 commit 20231ab
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 16 deletions.
9 changes: 2 additions & 7 deletions libs/coin-framework/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,9 @@ export type Transaction = {
recipient: string;
amount: bigint;
fee: bigint;
supplement?: unknown;
};
} & Record<string, unknown>; // Field containing dedicated value for each blockchain

// TODO rename start to minHeight
// and add a `token: string` field to the pagination if we really need to support pagination
// (which is not the case for now)
// for now start is used as a minHeight from which we want to fetch ALL operations
// limit is unused for now
// TODO add a `token: string` field to the pagination if we really need to support pagination (which is not the case for now)
// see design document at https://ledgerhq.atlassian.net/wiki/spaces/BE/pages/5446205788/coin-modules+lama-adapter+APIs+refinements
export type Pagination = { minHeight: number };
export type Api = {
Expand Down
2 changes: 2 additions & 0 deletions libs/coin-modules/coin-xrp/src/api/index.integ.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ describe("Xrp Api", () => {
recipient: "rKRtUG15iBsCQRgrkeUEg5oX4Ae2zWZ89z",
amount: BigInt(10),
fee: BigInt(1),
memos: [{ data: "01", format: "02", type: "03" }],
destinationTag: 123,
});

// Then
Expand Down
2 changes: 1 addition & 1 deletion libs/coin-modules/coin-xrp/src/bridge/signOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const buildSignOperation =
recipient: transaction.recipient,
amount: BigInt(transaction.amount.toString()),
fee: BigInt(fee.toString()),
tag: transaction.tag,
destinationTag: transaction.tag,
},
publicKey,
);
Expand Down
12 changes: 10 additions & 2 deletions libs/coin-modules/coin-xrp/src/logic/craftTransaction.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { craftTransaction } from "./craftTransaction";

import { decode } from "ripple-binary-codec";
jest.mock("../network", () => ({
getLedgerIndex: () => 1,
}));
Expand Down Expand Up @@ -46,6 +46,8 @@ describe("craftTransaction", () => {
recipient: "rJe1St1G6BWMFmdrrcT7NdD3XT1NxTMEWN",
amount: BigInt(100_000_000),
fee: BigInt(100),
memos: [{ data: "01", format: "02", type: "03" }],
destinationTag: 123,
};
const pubKey = "public_key";

Expand All @@ -59,12 +61,18 @@ describe("craftTransaction", () => {
Account: "rPDf6SQStnNmw1knCu1ei7h6BcDAEUUqn5",
Amount: "100000000",
Destination: "rJe1St1G6BWMFmdrrcT7NdD3XT1NxTMEWN",
DestinationTag: undefined,
DestinationTag: 123,
Fee: "100",
Flags: 2147483648,
Sequence: 2,
LastLedgerSequence: 21,
Memos: [{ Memo: { MemoData: "01", MemoFormat: "02", MemoType: "03" } }],
});
expect(result.serializedTransaction).toBeDefined();
const binDecodedTx = decode(result.serializedTransaction);
expect(binDecodedTx.Memos).toEqual([
{ Memo: { MemoData: "01", MemoFormat: "02", MemoType: "03" } },
]);
expect(binDecodedTx.DestinationTag).toEqual(123);
});
});
44 changes: 38 additions & 6 deletions libs/coin-modules/coin-xrp/src/logic/craftTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,34 @@ import { UINT32_MAX, validateTag } from "./utils";
const LEDGER_OFFSET = 20;

const { TRANSACTION_TYPES } = XrplDefinitions;
type Memo = {
MemoData?: string;
MemoFormat?: string;
MemoType?: string;
};
type MemoWrapper = {
Memo: Memo;
};
type XrplTransaction = {
TransactionType: keyof typeof TRANSACTION_TYPES;
Flags: number;
Account: string;
Amount: string;
Destination: string;
DestinationTag: number | undefined;
DestinationTag?: number;
Fee: string;
Sequence: number;
LastLedgerSequence: number;
SigningPubKey?: string;
TxnSignature?: string;
Memos?: MemoWrapper[];
};

type MemoInput = {
data?: string;
format?: string;
type?: string;
};
export async function craftTransaction(
account: {
address: string;
Expand All @@ -31,31 +45,49 @@ export async function craftTransaction(
recipient: string;
amount: bigint;
fee: bigint;
tag?: number | null | undefined;
destinationTag?: number | null | undefined;
memos?: MemoInput[];
},
publicKey?: string,
): Promise<{
xrplTransaction: XrplTransaction;
serializedTransaction: string;
}> {
const tag = transaction.tag ? transaction.tag : undefined;
const xrplTransaction: XrplTransaction = {
TransactionType: "Payment",
Account: account.address,
Amount: transaction.amount.toString(),
Destination: transaction.recipient,
DestinationTag: tag,
Fee: transaction.fee.toString(),
Flags: 2147483648,
Sequence: account.nextSequenceNumber,
LastLedgerSequence: (await getLedgerIndex()) + LEDGER_OFFSET,
};

if (tag) {
function memoMapper(memoInput: MemoInput): MemoWrapper {
const memo: Memo = {};
if (memoInput.data) {
memo.MemoData = memoInput.data;
}
if (memoInput.format) {
memo.MemoFormat = memoInput.format;
}
if (memoInput.type) {
memo.MemoType = memoInput.type;
}
return { Memo: memo };
}

if (transaction.memos) {
xrplTransaction.Memos = transaction.memos.map(memoMapper);
}

if (transaction.destinationTag) {
invariant(
validateTag(new BigNumber(tag)),
validateTag(new BigNumber(transaction.destinationTag)),
`tag is set but is not in a valid format, should be between [0 - ${UINT32_MAX.toString()}]`,
);
xrplTransaction.DestinationTag = transaction.destinationTag;
}

const serializedTransaction = publicKey
Expand Down

0 comments on commit 20231ab

Please sign in to comment.