Skip to content

Commit

Permalink
[XRP] Expose functions for Alpaca (#7160)
Browse files Browse the repository at this point in the history
* feat: separate core logic from bridge logic

Signed-off-by: Stéphane Prohaszka <[email protected]>

* feat: move files to correct sub dir

Signed-off-by: Stéphane Prohaszka <[email protected]>

* feat: reorganize module

Signed-off-by: Stéphane Prohaszka <[email protected]>

* feat: update Polkadot Alpaca api and add consistency with Ripple

Signed-off-by: Stéphane Prohaszka <[email protected]>

* fix: missing export

Signed-off-by: Stéphane Prohaszka <[email protected]>

* feat: returns operations content

Signed-off-by: Stéphane Prohaszka <[email protected]>

* fix: unimported

Signed-off-by: Stéphane Prohaszka <[email protected]>

* chore: cleanup

Signed-off-by: Stéphane Prohaszka <[email protected]>

* chore: changeset

Signed-off-by: Stéphane Prohaszka <[email protected]>

* fix: xrp dataset import

Signed-off-by: Stéphane Prohaszka <[email protected]>

* feat: add unit test to core

Signed-off-by: Stéphane Prohaszka <[email protected]>

* feat: add more unit test

Signed-off-by: Stéphane Prohaszka <[email protected]>

---------

Signed-off-by: Stéphane Prohaszka <[email protected]>
  • Loading branch information
sprohaszka-ledger authored Jun 27, 2024
1 parent f6cea5f commit b14d37d
Show file tree
Hide file tree
Showing 65 changed files with 1,170 additions and 392 deletions.
7 changes: 7 additions & 0 deletions .changeset/silly-toes-lick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@ledgerhq/coin-polkadot": minor
"@ledgerhq/coin-xrp": minor
"@ledgerhq/live-common": minor
---

Expose API on CoinModule (Xrp and Polkadot) so they can be used in a dedicated service
4 changes: 1 addition & 3 deletions libs/coin-modules/coin-polkadot/.unimportedrc.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
{
"entry": [
"src/api/index.ts",
"src/bridge/index.ts",
"src/bridge/deviceTransactionConfig.ts",
"src/bridge/formatters.ts",
"src/signer/index.ts",
"src/test/cli.ts",
"src/test/index.ts",
"src/test/specs.ts",
"src/types/errors.ts",
"src/index.ts"
],
"ignorePatterns": [
Expand Down
8 changes: 8 additions & 0 deletions libs/coin-modules/coin-polkadot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@
"lib-es/*": [
"lib-es/*"
],
"api": [
"lib/api/index"
],
"logic": [
"lib/logic/index"
],
Expand All @@ -91,6 +94,7 @@
],
"*": [
"lib/*",
"lib/api/*",
"lib/bridge/*",
"lib/logic/*",
"lib/signer/*",
Expand All @@ -102,6 +106,10 @@
"exports": {
"./lib/*": "./lib/*.js",
"./lib-es/*": "./lib-es/*.js",
"./api": {
"require": "./lib/api/index.js",
"default": "./lib-es/api/index.js"
},
"./deviceTransactionConfig": {
"require": "./lib/bridge/deviceTransactionConfig.js",
"default": "./lib-es/bridge/deviceTransactionConfig.js"
Expand Down
63 changes: 63 additions & 0 deletions libs/coin-modules/coin-polkadot/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { PolkadotConfig, setCoinConfig } from "../config";
import {
broadcast,
craftEstimationTransaction,
craftTransaction,
defaultExtrinsicArg,
estimateFees,
getBalance,
listOperations,
type Operation,
} from "../logic";

export type Api = {
broadcast: (tx: string) => Promise<string>;
combine: (tx: string, signature: string, pubkey: string) => string;
craftTransaction: (
address: string,
transaction: {
recipient: string;
amount: bigint;
fee: bigint;
},
) => Promise<string>;
estimateFees: (addr: string, amount: bigint) => Promise<bigint>;
getBalance: (address: string) => Promise<bigint>;
listOperations: (address: string, blockHeight: number) => Promise<Operation[]>;
};
export function createApi(config: PolkadotConfig): Api {
setCoinConfig(() => ({ ...config, status: { type: "active" } }));

return {
broadcast,
combine: () => {
throw new Error("UnsupportedMethod");
},
craftTransaction: craft,
estimateFees: estimate,
getBalance,
listOperations,
};
}

async function craft(
address: string,
transaction: {
recipient: string;
amount: bigint;
},
): Promise<string> {
const extrinsicArg = defaultExtrinsicArg(transaction.amount, transaction.recipient);
//TODO: Retrieve correctly the nonce via a call to the node `await api.rpc.system.accountNextIndex(address)`
const nonce = -1;
const tx = await craftTransaction(address, nonce, extrinsicArg);
const extrinsic = tx.registry.createType("Extrinsic", tx.unsigned, {
version: tx.unsigned.version,
});
return extrinsic.toHex();
}

async function estimate(addr: string, amount: bigint): Promise<bigint> {
const tx = await craftEstimationTransaction(addr, amount);
return estimateFees(tx);
}
2 changes: 1 addition & 1 deletion libs/coin-modules/coin-polkadot/src/logic/broadcast.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import polkadotAPI from "../network";
import { loadPolkadotCrypto } from "./polkadot-crypto";

export default async function broadcast(signature: string): Promise<string> {
export async function broadcast(signature: string): Promise<string> {
await loadPolkadotCrypto();
return await polkadotAPI.submitExtrinsic(signature);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { loadPolkadotCrypto } from "./polkadot-crypto";
import estimateFees from "./estimateFees";
import { estimateFees } from "./estimateFees";
import {
fixtureChainSpec,
fixtureTxMaterialWithMetadata,
Expand Down
5 changes: 1 addition & 4 deletions libs/coin-modules/coin-polkadot/src/logic/estimateFees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ import { fakeSignExtrinsic } from "./signTransaction";
import polkadotAPI from "../network";
import { CoreTransaction } from "../types";

export default async function estimateFees({
unsigned,
registry,
}: CoreTransaction): Promise<bigint> {
export async function estimateFees({ unsigned, registry }: CoreTransaction): Promise<bigint> {
await loadPolkadotCrypto();

const fakeSignedTx = await fakeSignExtrinsic(unsigned, registry);
Expand Down
6 changes: 6 additions & 0 deletions libs/coin-modules/coin-polkadot/src/logic/getBalance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import network from "../network";

export async function getBalance(addr: string): Promise<bigint> {
const balances = await network.getBalances(addr);
return BigInt(balances.balance.toString());
}
8 changes: 4 additions & 4 deletions libs/coin-modules/coin-polkadot/src/logic/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export {
} from "./craftTransaction";
export { signExtrinsic } from "./signTransaction";

import broadcast from "./broadcast";
import estimateFees from "./estimateFees";

export { broadcast, estimateFees };
export { broadcast } from "./broadcast";
export { getBalance } from "./getBalance";
export { estimateFees } from "./estimateFees";
export { listOperations, type Operation } from "./listOperations";
49 changes: 49 additions & 0 deletions libs/coin-modules/coin-polkadot/src/logic/listOperations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import network from "../network";
import { PolkadotOperation } from "../types";

export type Operation = {
hash: string;
address: string;
type: string;
value: bigint;
fee: bigint;
blockHeight: number;
senders: string[];
recipients: string[];
date: Date;
transactionSequenceNumber: number;
};

export async function listOperations(addr: string): Promise<Operation[]> {
//The accountId is used to map Operations to Live types.
const fakeAccountId = "";
const operations = await network.getOperations(fakeAccountId, addr);

return operations.map(convertToCoreOperation(addr));
}

const convertToCoreOperation = (address: string) => (operation: PolkadotOperation) => {
const {
hash,
type,
value,
fee,
blockHeight,
senders,
recipients,
date,
transactionSequenceNumber,
} = operation;
return {
hash,
address,
type,
value: BigInt(value.toString()),
fee: BigInt(fee.toString()),
blockHeight: blockHeight ?? 0,
senders,
recipients,
date,
transactionSequenceNumber: transactionSequenceNumber ?? 0,
};
};
11 changes: 11 additions & 0 deletions libs/coin-modules/coin-polkadot/src/network/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { makeLRUCache, minutes, hours } from "@ledgerhq/live-network/cache";
import { getOperations as bisonGetOperations } from "./bisontrails";
import {
getAccount as sidecardGetAccount,
getBalances as sidecardGetBalances,
getMinimumBondBalance as sidecarGetMinimumBondBalance,
getRegistry as sidecarGetRegistry,
getStakingProgress as sidecarGetStakingProgress,
Expand Down Expand Up @@ -36,6 +37,14 @@ type PolkadotAPIAccount = {
nominations: PolkadotNomination[];
};

type PolkadotAPIBalanceInfo = {
blockHeight: number;
balance: BigNumber;
spendableBalance: BigNumber;
nonce: number;
lockedBalance: BigNumber;
};

type CacheOpts = {
force: boolean;
};
Expand Down Expand Up @@ -96,6 +105,8 @@ const shortenMetadata = async (transaction: string): Promise<string> => {

export default {
getAccount: async (address: string): Promise<PolkadotAPIAccount> => sidecardGetAccount(address),
getBalances: async (address: string): Promise<PolkadotAPIBalanceInfo> =>
sidecardGetBalances(address),
getOperations: bisonGetOperations,
getMinimumBondBalance,
getRegistry,
Expand Down
2 changes: 1 addition & 1 deletion libs/coin-modules/coin-polkadot/src/network/sidecar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ export const getAccount = async (addr: string) => {
* @async
* @param {*} addr - the account address
*/
const getBalances = async (addr: string) => {
export const getBalances = async (addr: string) => {
const balanceInfo = await fetchBalanceInfo(addr);
// Locked is the highest value among locks
const totalLocked = balanceInfo.locks.reduce((total, lock) => {
Expand Down
15 changes: 6 additions & 9 deletions libs/coin-modules/coin-xrp/.unimportedrc.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
{
"entry": [
"src/api/index.ts",
"src/bridge/js.ts",
"src/cli-transaction.ts",
"src/deviceTransactionConfig.ts",
"src/errors.ts",
"src/hw-getAddress.ts",
"src/serialization.ts",
"src/specs.ts",
"src/transaction.ts",
"src/datasets/dataset-1.ts"
"src/bridge/index.ts",
"src/bridge/deviceTransactionConfig.ts",
"src/datasets/dataset-1.ts",
"src/signer/index.ts",
"src/test/index.ts",
"src/index.ts"
],
"ignorePatterns": [
"**/node_modules/**",
Expand Down
6 changes: 4 additions & 2 deletions libs/coin-modules/coin-xrp/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
/** @type {import('ts-jest/dist/types').JestConfigWithTsJest} */
// `workerThreads: true` is required for validating object with `bigint` values
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
testPathIgnorePatterns: ["lib/", "lib-es/"],
testPathIgnorePatterns: ["lib/", "lib-es/", ".*\\.integ\\.test\\.[tj]s"],
workerThreads: true,
};
7 changes: 7 additions & 0 deletions libs/coin-modules/coin-xrp/jest.integ.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/** @type {import('ts-jest/dist/types').JestConfigWithTsJest} */
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
testRegex: ".integ.test.ts$",
testPathIgnorePatterns: ["lib/", "lib-es/"],
};
59 changes: 58 additions & 1 deletion libs/coin-modules/coin-xrp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,73 @@
"lib-es/*": [
"lib-es/*"
],
"api": [
"lib/api/index"
],
"deviceTransactionConfig": [
"lib/bridge/deviceTransactionConfig"
],
"logic": [
"lib/logic/index"
],
"specs": [
"lib/test/bot-specs"
],
"transaction": [
"lib/bridge/transaction"
],
"types": [
"lib/types/index"
],
"*": [
"lib/*"
"lib/*",
"lib/bridge/*",
"lib/logic/*",
"lib/signer/*",
"lib/test/*",
"lib/types/*"
]
}
},
"exports": {
"./lib/*": "./lib/*.js",
"./lib-es/*": "./lib-es/*.js",
"./api": {
"require": "./lib/api/index.js",
"default": "./lib-es/api/index.js"
},
"./deviceTransactionConfig": {
"require": "./lib/bridge/deviceTransactionConfig.js",
"default": "./lib-es/bridge/deviceTransactionConfig.js"
},
"./logic": {
"require": "./lib/logic/index.js",
"default": "./lib-es/logic/index.js"
},
"./signer": {
"require": "./lib/signer/index.js",
"default": "./lib-es/signer/index.js"
},
"./specs": {
"require": "./lib/test/bot-specs.js",
"default": "./lib-es/test/bot-specs.js"
},
"./transaction": {
"require": "./lib/bridge/transaction.js",
"default": "./lib-es/bridge/transaction.js"
},
"./types": {
"require": "./lib/types/index.js",
"default": "./lib-es/types/index.js"
},
"./*": {
"require": "./lib/*.js",
"default": "./lib-es/*.js"
},
".": {
"require": "./lib/index.js",
"default": "./lib-es/index.js"
},
"./package.json": "./package.json"
},
"license": "Apache-2.0",
Expand All @@ -57,6 +112,7 @@
"rxjs": "^7.8.1"
},
"devDependencies": {
"@faker-js/faker": "^8.4.1",
"@ledgerhq/types-cryptoassets": "workspace:^",
"@types/invariant": "^2.2.37",
"@types/jest": "^29.5.12",
Expand All @@ -75,6 +131,7 @@
"lint": "eslint ./src --no-error-on-unmatched-pattern --ext .ts,.tsx --cache",
"lint:fix": "pnpm lint --fix",
"test": "jest",
"test-integ": "jest --config=jest.integ.config.js",
"typecheck": "tsc --noEmit",
"unimported": "unimported"
}
Expand Down
3 changes: 0 additions & 3 deletions libs/coin-modules/coin-xrp/please-add-coverage.test.ts

This file was deleted.

Loading

0 comments on commit b14d37d

Please sign in to comment.