Skip to content

Commit

Permalink
[XTZ] Alpaca manage staking transaction (#8432)
Browse files Browse the repository at this point in the history
* feat: alpaca allow transaction crafting for staking

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

* chore: changeset

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

---------

Signed-off-by: Stéphane Prohaszka <[email protected]>
  • Loading branch information
sprohaszka-ledger authored Nov 21, 2024
1 parent ed57628 commit daa059a
Show file tree
Hide file tree
Showing 14 changed files with 78 additions and 53 deletions.
9 changes: 9 additions & 0 deletions .changeset/slow-tips-camp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@ledgerhq/coin-polkadot": minor
"@ledgerhq/coin-stellar": minor
"@ledgerhq/coin-tezos": minor
"@ledgerhq/coin-xrp": minor
"@ledgerhq/coin-framework": minor
---

Alpaca allows delegate and undelegate tx crafting
5 changes: 5 additions & 0 deletions libs/coin-framework/src/api/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export class IncorrectTypeError extends Error {
constructor(message?: string) {
super(`IncorrectType: ${message}`);
}
}
1 change: 1 addition & 0 deletions libs/coin-framework/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
* One consumer of this API is Alpaca.
*/

export * from "./errors";
export * from "./types";
2 changes: 1 addition & 1 deletion libs/coin-framework/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export type Operation = {
};

export type Transaction = {
mode: string;
type: string;
recipient: string;
amount: bigint;
fee: bigint;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ describe("Polkadot Api", () => {
it("returns a raw transaction", async () => {
// When
const result = await module.craftTransaction(address, {
mode: "send",
type: "send",
recipient: "16YreVmGhM8mNMqnsvK7rn7b1e4SKYsTfFUn4UfCZ65BgDjh",
amount: BigInt(10),
fee: BigInt(1),
Expand Down
13 changes: 2 additions & 11 deletions libs/coin-modules/coin-polkadot/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Api } from "@ledgerhq/coin-framework/api/index";
import type { Api, Transaction as ApiTransaction } from "@ledgerhq/coin-framework/api/index";
import coinConfig, { type PolkadotConfig } from "../config";
import {
broadcast,
Expand Down Expand Up @@ -27,16 +27,7 @@ export function createApi(config: PolkadotConfig): Api {
};
}

async function craft(
address: string,
transaction: {
mode: string;
recipient: string;
amount: bigint;
fee: bigint;
supplement?: unknown;
},
): Promise<string> {
async function craft(address: string, transaction: ApiTransaction): 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 = 0;
Expand Down
2 changes: 1 addition & 1 deletion libs/coin-modules/coin-stellar/src/api/index.integ.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ describe("Stellar Api", () => {
it("returns a raw transaction", async () => {
// When
const result = await module.craftTransaction(address, {
mode: "send",
type: "send",
recipient: "GD6QELUZPSKPRWVXOQ3F6GBF4OBRMCHO5PHREXH4ZRTPJAG7V5MD7JGX",
amount: BigInt(1_000_000),
fee: BigInt(100),
Expand Down
13 changes: 2 additions & 11 deletions libs/coin-modules/coin-stellar/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Api } from "@ledgerhq/coin-framework/api/index";
import type { Api, Transaction as ApiTransaction } from "@ledgerhq/coin-framework/api/index";
import coinConfig, { type StellarConfig } from "../config";
import {
broadcast,
Expand Down Expand Up @@ -33,16 +33,7 @@ type Supplement = {
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> {
async function craft(address: string, transaction: ApiTransaction): Promise<string> {
const supplement = isSupplement(transaction.supplement)
? {
assetCode: transaction.supplement?.assetCode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export async function buildTransaction(account: Account, transaction: Transactio
const { transaction: built } = await craftTransaction(
{ address: account.freshAddress },
{
mode,
type: mode,
recipient,
amount: BigInt(amount.toString()),
fee: BigInt(fees.toString()),
Expand Down
6 changes: 3 additions & 3 deletions libs/coin-modules/coin-stellar/src/logic/craftTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export async function craftTransaction(
address: string;
},
transaction: {
mode: string;
type: string;
recipient: string;
amount: bigint;
fee: bigint;
Expand All @@ -29,7 +29,7 @@ export async function craftTransaction(
memoValue?: string | null | undefined;
},
): Promise<{ transaction: StellarSdkTransaction; xdr: string }> {
const { amount, recipient, fee, memoType, memoValue, mode, assetCode, assetIssuer } = transaction;
const { amount, recipient, fee, memoType, memoValue, type, assetCode, assetIssuer } = transaction;

const source = await loadAccount(account.address);

Expand All @@ -40,7 +40,7 @@ export async function craftTransaction(
const transactionBuilder = buildTransactionBuilder(source, fee);
let operation: xdr.Operation<StellarSdkOperation.ChangeTrust> | null = null;

if (mode === "changeTrust") {
if (type === "changeTrust") {
if (!assetCode || !assetIssuer) {
throw new StellarAssetRequired("");
}
Expand Down
37 changes: 31 additions & 6 deletions libs/coin-modules/coin-tezos/src/api/index.integ.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Api } from "@ledgerhq/coin-framework/api/index";
import { IncorrectTypeError, type Api } from "@ledgerhq/coin-framework/api/index";
import { createApi } from ".";
/**
* https://teztnets.com/ghostnet-about
Expand Down Expand Up @@ -82,19 +82,44 @@ describe("Tezos Api", () => {
});

describe("craftTransaction", () => {
it("returns a raw transaction", async () => {
it.each([
{
type: "send",
rawTx:
"6c0053ddb3b3a89ed5c8d8326066032beac6de225c9e010300000a0000a31e81ac3425310e3274a4698a793b2839dc0afa00",
},
{
type: "delegate",
rawTx:
"6e0053ddb3b3a89ed5c8d8326066032beac6de225c9e01030000ff00a31e81ac3425310e3274a4698a793b2839dc0afa",
},
{
type: "undelegate",
rawTx: "6e0053ddb3b3a89ed5c8d8326066032beac6de225c9e0103000000",
},
])("returns a raw transaction with $type", async ({ type, rawTx }) => {
// When
const result = await module.craftTransaction(address, {
mode: "send",
type,
recipient: "tz1aWXP237BLwNHJcCD4b3DutCevhqq2T1Z9",
amount: BigInt(10),
fee: BigInt(1),
});

// Then
expect(result.slice(64)).toEqual(
"6c0053ddb3b3a89ed5c8d8326066032beac6de225c9e010300000a0000a31e81ac3425310e3274a4698a793b2839dc0afa00",
);
expect(result.slice(64)).toEqual(rawTx);
});

it("throws an error if type in 'send' or 'delegate' or 'undelegate'", async () => {
// When
await expect(
module.craftTransaction(address, {
type: "WHATEVERTYPE",
recipient: "tz1aWXP237BLwNHJcCD4b3DutCevhqq2T1Z9",
amount: BigInt(10),
fee: BigInt(1),
}),
).rejects.toThrow(IncorrectTypeError);
});
});
});
21 changes: 14 additions & 7 deletions libs/coin-modules/coin-tezos/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import type { Api } from "@ledgerhq/coin-framework/api/index";
import {
IncorrectTypeError,
type Api,
type Transaction as ApiTransaction,
} from "@ledgerhq/coin-framework/api/index";
import coinConfig, { type TezosConfig } from "../config";
import {
broadcast,
Expand Down Expand Up @@ -26,17 +30,20 @@ export function createApi(config: TezosConfig): Api {
};
}

function isTezosTransactionType(type: string): type is "send" | "delegate" | "undelegate" {
return ["send", "delegate", "undelegate"].includes(type);
}
async function craft(
address: string,
transaction: {
recipient: string;
amount: bigint;
fee: bigint;
},
{ type, recipient, amount, fee }: ApiTransaction,
): Promise<string> {
if (!isTezosTransactionType(type)) {
throw new IncorrectTypeError(type);
}

const { contents } = await craftTransaction(
{ address },
{ ...transaction, type: "send", fee: { fees: transaction.fee.toString() } },
{ recipient, amount, type, fee: { fees: fee.toString() } },
);
return rawEncode(contents);
}
Expand Down
2 changes: 1 addition & 1 deletion libs/coin-modules/coin-xrp/src/api/index.integ.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ describe("Xrp Api", () => {
it("returns a raw transaction", async () => {
// When
const result = await module.craftTransaction(address, {
mode: "send",
type: "send",
recipient: "rKRtUG15iBsCQRgrkeUEg5oX4Ae2zWZ89z",
amount: BigInt(10),
fee: BigInt(1),
Expand Down
16 changes: 6 additions & 10 deletions libs/coin-modules/coin-xrp/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import type { Api, Operation } from "@ledgerhq/coin-framework/api/index";
import type {
Api,
Operation,
Transaction as ApiTransaction,
} from "@ledgerhq/coin-framework/api/index";
import coinConfig, { type XrpConfig } from "../config";
import {
broadcast,
Expand All @@ -25,15 +29,7 @@ export function createApi(config: XrpConfig): Api {
};
}

async function craft(
address: string,
transaction: {
mode: string;
recipient: string;
amount: bigint;
fee: bigint;
},
): Promise<string> {
async function craft(address: string, transaction: ApiTransaction): Promise<string> {
const nextSequenceNumber = await getNextValidSequence(address);
const tx = await craftTransaction({ address, nextSequenceNumber }, transaction);
return tx.serializedTransaction;
Expand Down

0 comments on commit daa059a

Please sign in to comment.