Skip to content

Commit

Permalink
[XLM] Adaptation for Alpaca (#7268)
Browse files Browse the repository at this point in the history
* feat: adapt code organization for alpaca calls

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

* feat: add getBalance

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

* fix: missing export

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

* feat(xlm): add listOperations

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

* feat: add craftTransaction

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

* chore: remove generics as it will forbid the use of generic Api creation

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

* feat(xlm): add lastBlock function

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

* feat(xlm): add combine function

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

* feat(xlm): add estimation fees

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

* fix: missing export

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

* fix(xlm): config typo

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

* fix: activate useAllAmount

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

* chore: changeset

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

* chore: update test

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

* fix: rebase issue

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

* chore: update bot device to NanoSP

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

---------

Signed-off-by: Stéphane Prohaszka <[email protected]>
  • Loading branch information
sprohaszka-ledger committed Aug 9, 2024
1 parent 2dd4825 commit 9c2f1b3
Show file tree
Hide file tree
Showing 58 changed files with 965 additions and 544 deletions.
10 changes: 10 additions & 0 deletions .changeset/thick-boats-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@ledgerhq/coin-stellar": minor
"@ledgerhq/live-common": minor
"@ledgerhq/coin-framework": minor
"@ledgerhq/coin-polkadot": patch
"@ledgerhq/coin-tezos": patch
"@ledgerhq/coin-xrp": patch
---

Prepare CoinModule Stellar for Alpaca
17 changes: 9 additions & 8 deletions libs/coin-framework/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,18 @@ export type Operation = {
transactionSequenceNumber: number;
};

export type Transaction = {
mode: string;
recipient: string;
amount: bigint;
fee: bigint;
supplement?: unknown;
};

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>;
craftTransaction: (address: string, transaction: Transaction, pubkey?: string) => Promise<string>;
estimateFees: (addr: string, amount: bigint) => Promise<bigint>;
getBalance: (address: string) => Promise<bigint>;
lastBlock: () => Promise<BlockInfo>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ describe("Polkadot Api", () => {
it("returns a raw transaction", async () => {
// When
const result = await module.craftTransaction(address, {
mode: "send",
recipient: "16YreVmGhM8mNMqnsvK7rn7b1e4SKYsTfFUn4UfCZ65BgDjh",
amount: BigInt(10),
fee: BigInt(1),
Expand Down
3 changes: 3 additions & 0 deletions libs/coin-modules/coin-polkadot/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ export function createApi(config: PolkadotConfig): Api {
async function craft(
address: string,
transaction: {
mode: string;
recipient: string;
amount: bigint;
fee: bigint;
supplement?: unknown;
},
): Promise<string> {
const extrinsicArg = defaultExtrinsicArg(transaction.amount, transaction.recipient);
Expand Down
28 changes: 6 additions & 22 deletions libs/coin-modules/coin-stellar/.unimportedrc.json
Original file line number Diff line number Diff line change
@@ -1,34 +1,18 @@
{
"entry": [
"src/deviceTransactionConfig.ts",
"src/errors.ts",
"src/hw-getAddress.ts",
"src/serialization.ts",
"src/specs.ts",
"src/transaction.ts"
"src/api/index.ts",
"src/bridge/index.ts",
"src/bridge/deviceTransactionConfig.ts",
"src/signer/index.ts",
"src/test/index.ts",
"src/index.ts"
],
"ignorePatterns": [
"**/node_modules/**",
"**/*.fixture.ts",
"**/*.mock.ts",
"**/*.test.{js,jsx,ts,tsx}"
],
"ignoreUnresolved": [],
"ignoreUnimported": [
"src/bridge/index.ts",
"src/broadcast.ts",
"src/buildOptimisticOperation.ts",
"src/buildTransaction.ts",
"src/cli.ts",
"src/config.ts",
"src/createTransaction.ts",
"src/estimateMaxSpendable.ts",
"src/getTransactionStatus.ts",
"src/prepareTransaction.ts",
"src/signOperation.ts",
"src/synchronization.ts",
"src/tokens.ts"
],
"ignoreUnused": [
"rxjs"
]
Expand Down
107 changes: 83 additions & 24 deletions libs/coin-modules/coin-stellar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,29 +31,6 @@
"typecheck": "tsc --noEmit",
"unimported": "unimported"
},
"dependencies": {
"@ledgerhq/coin-framework": "workspace:^",
"@ledgerhq/cryptoassets": "workspace:^",
"@ledgerhq/devices": "workspace:^",
"@ledgerhq/errors": "workspace:^",
"@ledgerhq/live-env": "workspace:^",
"@ledgerhq/live-network": "workspace:^",
"@ledgerhq/logs": "workspace:^",
"@ledgerhq/types-cryptoassets": "workspace:^",
"@ledgerhq/types-live": "workspace:^",
"@stellar/stellar-sdk": "^11.3.0",
"bignumber.js": "^9.1.2",
"expect": "^27.4.6",
"invariant": "^2.2.2",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@faker-js/faker": "^8.4.1",
"@types/invariant": "^2.2.2",
"@types/jest": "^29.5.10",
"jest": "^29.7.0",
"ts-jest": "^29.1.1"
},
"publishConfig": {
"access": "public"
},
Expand All @@ -65,18 +42,100 @@
"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"
},
"./errors": {
"require": "./lib/types/errors.js",
"default": "./lib-es/types/errors.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"
},
"dependencies": {
"@ledgerhq/coin-framework": "workspace:^",
"@ledgerhq/cryptoassets": "workspace:^",
"@ledgerhq/devices": "workspace:^",
"@ledgerhq/errors": "workspace:^",
"@ledgerhq/live-network": "workspace:^",
"@ledgerhq/logs": "workspace:^",
"@ledgerhq/types-cryptoassets": "workspace:^",
"@ledgerhq/types-live": "workspace:^",
"@stellar/stellar-sdk": "^11.3.0",
"bignumber.js": "^9.1.2",
"expect": "^27.4.6",
"invariant": "^2.2.2",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@faker-js/faker": "^8.4.1",
"@types/invariant": "^2.2.2",
"@types/jest": "^29.5.10",
"@types/node": "^20.8.10",
"jest": "^29.7.0",
"ts-jest": "^29.1.1"
}
}
86 changes: 86 additions & 0 deletions libs/coin-modules/coin-stellar/src/api/index.integ.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import type { Api } from "@ledgerhq/coin-framework/api/index";
import { createApi } from ".";

describe("Stellar Api", () => {
let module: Api;
const address = "GD6QELUZPSKPRWVXOQ3F6GBF4OBRMCHO5PHREXH4ZRTPJAG7V5MD7JGX";

beforeAll(() => {
module = createApi({
explorer: {
url: "https://horizon-testnet.stellar.org/",
},
});
});

describe("estimateFees", () => {
it("returns a default value", async () => {
// Given
const amount = BigInt(100_000);

// When
const result = await module.estimateFees(address, amount);

// Then
expect(result).toEqual(BigInt(100));
});
});

describe("listOperations", () => {
it("returns a list regarding address parameter", async () => {
// When
const result = await module.listOperations(address, 0);

// Then
expect(result.length).toBeGreaterThanOrEqual(1);
result.forEach(operation => {
expect(operation.address).toEqual(address);
const isSenderOrReceipt =
operation.senders.includes(address) || operation.recipients.includes(address);
expect(isSenderOrReceipt).toBeTruthy();
});
});
});

describe("lastBlock", () => {
it("returns last block info", async () => {
// When
const result = await module.lastBlock();

// Then
expect(result.hash).toBeDefined();
expect(result.height).toBeDefined();
expect(result.time).toBeInstanceOf(Date);
});
});

describe("getBalance", () => {
it("returns a list regarding address parameter", async () => {
// When
const result = await module.getBalance(address);

// Then
expect(result).toBeGreaterThan(0);
});
});

describe("craftTransaction", () => {
it("returns a raw transaction", async () => {
// When
const result = await module.craftTransaction(address, {
mode: "send",
recipient: "GD6QELUZPSKPRWVXOQ3F6GBF4OBRMCHO5PHREXH4ZRTPJAG7V5MD7JGX",
amount: BigInt(1_000_000),
fee: BigInt(100),
});

// Then
expect(result.slice(0, 67)).toEqual(
"AAAAAgAAAAD9Ai6ZfJT42rd0Nl8YJeODFgju688SXPzMZvSA369YPwAAAGQAAHloAAA",
);
expect(result.slice(70)).toEqual(
"AAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAD9Ai6ZfJT42rd0Nl8YJeODFgju688SXPzMZvSA369YPwAAAAAAAAAAAA9CQAAAAAAAAAAA",
);
});
});
});
72 changes: 72 additions & 0 deletions libs/coin-modules/coin-stellar/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import type { Api } from "@ledgerhq/coin-framework/api/index";
import coinConfig, { type StellarConfig } from "../config";
import {
broadcast,
combine,
craftTransaction,
estimateFees,
getBalance,
listOperations,
lastBlock,
} from "../logic";

export function createApi(config: StellarConfig): Api {
coinConfig.setCoinConfig(() => ({ ...config, status: { type: "active" } }));

return {
broadcast,
combine: compose,
craftTransaction: craft,
estimateFees,
getBalance,
lastBlock,
listOperations,
};
}

type Supplement = {
assetCode?: string | undefined;
assetIssuer?: string | undefined;
memoType?: string | null | undefined;
memoValue?: string | null | undefined;
};
function isSupplement(supplement: unknown): supplement is Supplement {
return typeof supplement === "object";
}
async function craft(
address: string,
transaction: {
mode: string;
recipient: string;
amount: bigint;
fee: bigint;
supplement?: unknown;
},
): Promise<string> {
const supplement = isSupplement(transaction.supplement)
? {
assetCode: transaction.supplement?.assetCode,
assetIssuer: transaction.supplement?.assetIssuer,
memoType: transaction.supplement?.memoType,
memoValue: transaction.supplement?.memoValue,
}
: {};
const tx = await craftTransaction(
{ address },
{
...transaction,
assetCode: supplement?.assetCode,
assetIssuer: supplement?.assetIssuer,
memoType: supplement?.memoType,
memoValue: supplement?.memoValue,
},
);
return tx.xdr;
}

function compose(tx: string, signature: string, pubkey?: string): string {
if (!pubkey) {
throw new Error("Missing pubkey");
}
return combine(tx, signature, pubkey);
}
Loading

0 comments on commit 9c2f1b3

Please sign in to comment.