@ledgerhq/coin-internet_computer 1.9.0-nightly.6 → 1.9.0-nightly.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +13 -0
- package/lib/api/api.d.ts +6 -11
- package/lib/api/api.d.ts.map +1 -1
- package/lib/api/api.js +84 -64
- package/lib/api/api.js.map +1 -1
- package/lib/bridge/bridgeHelpers/account.d.ts.map +1 -1
- package/lib/bridge/bridgeHelpers/account.js +52 -33
- package/lib/bridge/bridgeHelpers/account.js.map +1 -1
- package/lib/bridge/bridgeHelpers/addresses.d.ts +0 -6
- package/lib/bridge/bridgeHelpers/addresses.d.ts.map +1 -1
- package/lib/bridge/bridgeHelpers/addresses.js +1 -29
- package/lib/bridge/bridgeHelpers/addresses.js.map +1 -1
- package/lib/bridge/broadcast.d.ts.map +1 -1
- package/lib/bridge/broadcast.js +22 -6
- package/lib/bridge/broadcast.js.map +1 -1
- package/lib/bridge/getTransactionStatus.d.ts.map +1 -1
- package/lib/bridge/getTransactionStatus.js +4 -3
- package/lib/bridge/getTransactionStatus.js.map +1 -1
- package/lib/bridge/prepareTransaction.d.ts.map +1 -1
- package/lib/bridge/prepareTransaction.js +2 -1
- package/lib/bridge/prepareTransaction.js.map +1 -1
- package/lib/bridge/signOperation.d.ts +2 -2
- package/lib/bridge/signOperation.d.ts.map +1 -1
- package/lib/bridge/signOperation.js +44 -13
- package/lib/bridge/signOperation.js.map +1 -1
- package/lib/consts.d.ts +2 -0
- package/lib/consts.d.ts.map +1 -1
- package/lib/consts.js +8 -1
- package/lib/consts.js.map +1 -1
- package/lib/hw-signMessage.d.ts.map +1 -1
- package/lib/hw-signMessage.js +1 -2
- package/lib/hw-signMessage.js.map +1 -1
- package/lib/types/signer.d.ts +1 -1
- package/lib/types/signer.d.ts.map +1 -1
- package/lib-es/api/api.d.ts +6 -11
- package/lib-es/api/api.d.ts.map +1 -1
- package/lib-es/api/api.js +81 -60
- package/lib-es/api/api.js.map +1 -1
- package/lib-es/bridge/bridgeHelpers/account.d.ts.map +1 -1
- package/lib-es/bridge/bridgeHelpers/account.js +52 -33
- package/lib-es/bridge/bridgeHelpers/account.js.map +1 -1
- package/lib-es/bridge/bridgeHelpers/addresses.d.ts +0 -6
- package/lib-es/bridge/bridgeHelpers/addresses.d.ts.map +1 -1
- package/lib-es/bridge/bridgeHelpers/addresses.js +0 -23
- package/lib-es/bridge/bridgeHelpers/addresses.js.map +1 -1
- package/lib-es/bridge/broadcast.d.ts.map +1 -1
- package/lib-es/bridge/broadcast.js +19 -6
- package/lib-es/bridge/broadcast.js.map +1 -1
- package/lib-es/bridge/getTransactionStatus.d.ts.map +1 -1
- package/lib-es/bridge/getTransactionStatus.js +2 -1
- package/lib-es/bridge/getTransactionStatus.js.map +1 -1
- package/lib-es/bridge/prepareTransaction.d.ts.map +1 -1
- package/lib-es/bridge/prepareTransaction.js +2 -1
- package/lib-es/bridge/prepareTransaction.js.map +1 -1
- package/lib-es/bridge/signOperation.d.ts +2 -2
- package/lib-es/bridge/signOperation.d.ts.map +1 -1
- package/lib-es/bridge/signOperation.js +41 -13
- package/lib-es/bridge/signOperation.js.map +1 -1
- package/lib-es/consts.d.ts +2 -0
- package/lib-es/consts.d.ts.map +1 -1
- package/lib-es/consts.js +3 -0
- package/lib-es/consts.js.map +1 -1
- package/lib-es/hw-signMessage.d.ts.map +1 -1
- package/lib-es/hw-signMessage.js +1 -2
- package/lib-es/hw-signMessage.js.map +1 -1
- package/lib-es/types/signer.d.ts +1 -1
- package/lib-es/types/signer.d.ts.map +1 -1
- package/package.json +5 -11
- package/src/api/api.ts +126 -71
- package/src/bridge/bridgeHelpers/account.ts +70 -43
- package/src/bridge/bridgeHelpers/addresses.ts +0 -25
- package/src/bridge/broadcast.ts +31 -6
- package/src/bridge/getTransactionStatus.ts +2 -1
- package/src/bridge/prepareTransaction.ts +2 -1
- package/src/bridge/signOperation.ts +68 -18
- package/src/consts.ts +10 -0
- package/src/hw-signMessage.ts +1 -6
- package/src/types/signer.ts +1 -1
- package/lib/bridge/bridgeHelpers/icpRosetta/index.d.ts +0 -30
- package/lib/bridge/bridgeHelpers/icpRosetta/index.d.ts.map +0 -1
- package/lib/bridge/bridgeHelpers/icpRosetta/index.js +0 -88
- package/lib/bridge/bridgeHelpers/icpRosetta/index.js.map +0 -1
- package/lib/bridge/bridgeHelpers/icpRosetta/types.d.ts +0 -145
- package/lib/bridge/bridgeHelpers/icpRosetta/types.d.ts.map +0 -1
- package/lib/bridge/bridgeHelpers/icpRosetta/types.js +0 -3
- package/lib/bridge/bridgeHelpers/icpRosetta/types.js.map +0 -1
- package/lib/bridge/bridgeHelpers/icpRosetta/utils.d.ts +0 -17
- package/lib/bridge/bridgeHelpers/icpRosetta/utils.d.ts.map +0 -1
- package/lib/bridge/bridgeHelpers/icpRosetta/utils.js +0 -155
- package/lib/bridge/bridgeHelpers/icpRosetta/utils.js.map +0 -1
- package/lib-es/bridge/bridgeHelpers/icpRosetta/index.d.ts +0 -30
- package/lib-es/bridge/bridgeHelpers/icpRosetta/index.d.ts.map +0 -1
- package/lib-es/bridge/bridgeHelpers/icpRosetta/index.js +0 -76
- package/lib-es/bridge/bridgeHelpers/icpRosetta/index.js.map +0 -1
- package/lib-es/bridge/bridgeHelpers/icpRosetta/types.d.ts +0 -145
- package/lib-es/bridge/bridgeHelpers/icpRosetta/types.d.ts.map +0 -1
- package/lib-es/bridge/bridgeHelpers/icpRosetta/types.js +0 -2
- package/lib-es/bridge/bridgeHelpers/icpRosetta/types.js.map +0 -1
- package/lib-es/bridge/bridgeHelpers/icpRosetta/utils.d.ts +0 -17
- package/lib-es/bridge/bridgeHelpers/icpRosetta/utils.d.ts.map +0 -1
- package/lib-es/bridge/bridgeHelpers/icpRosetta/utils.js +0 -123
- package/lib-es/bridge/bridgeHelpers/icpRosetta/utils.js.map +0 -1
- package/src/bridge/bridgeHelpers/icpRosetta/index.ts +0 -154
- package/src/bridge/bridgeHelpers/icpRosetta/types.ts +0 -166
- package/src/bridge/bridgeHelpers/icpRosetta/utils.ts +0 -151
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import type { GetAccountShape } from "@ledgerhq/coin-framework/bridge/jsHelpers";
|
|
2
2
|
import { decodeAccountId, encodeAccountId } from "@ledgerhq/coin-framework/account/index";
|
|
3
|
-
import {
|
|
3
|
+
import { fetchBalance, fetchBlockHeight, fetchTxns } from "../../api";
|
|
4
4
|
import flatMap from "lodash/flatMap";
|
|
5
|
-
import { Account } from "@ledgerhq/types-live";
|
|
5
|
+
import { Account, OperationType } from "@ledgerhq/types-live";
|
|
6
6
|
import BigNumber from "bignumber.js";
|
|
7
|
-
import { ICPRosettaGetTxnsHistoryResponse } from "./icpRosetta/types";
|
|
8
7
|
import { ICP_FEES } from "../../consts";
|
|
9
8
|
import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
|
|
10
9
|
import { normalizeEpochTimestamp } from "../../common-logic/utils";
|
|
11
10
|
import { InternetComputerOperation } from "../../types";
|
|
12
11
|
import invariant from "invariant";
|
|
13
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
deriveAddressFromPubkey,
|
|
14
|
+
hashTransaction,
|
|
15
|
+
TransactionWithId,
|
|
16
|
+
} from "@zondax/ledger-live-icp";
|
|
14
17
|
|
|
15
18
|
export const getAccountShape: GetAccountShape = async info => {
|
|
16
19
|
const { currency, derivationMode, rest = {}, initialAccount } = info;
|
|
@@ -32,17 +35,23 @@ export const getAccountShape: GetAccountShape = async info => {
|
|
|
32
35
|
// log("debug", `Generation account shape for ${address}`);
|
|
33
36
|
|
|
34
37
|
const blockHeight = await fetchBlockHeight();
|
|
35
|
-
const
|
|
36
|
-
const
|
|
38
|
+
const balance = await fetchBalance(address);
|
|
39
|
+
const txns = await fetchTxns(
|
|
40
|
+
address,
|
|
41
|
+
BigInt(blockHeight.toString()),
|
|
42
|
+
initialAccount ? BigInt(initialAccount.blockHeight.toString()) : undefined,
|
|
43
|
+
);
|
|
37
44
|
|
|
38
|
-
const txns = await fetchTxns(address);
|
|
39
45
|
const result: Partial<Account> = {
|
|
40
46
|
id: accountId,
|
|
41
|
-
balance
|
|
42
|
-
spendableBalance:
|
|
43
|
-
operations: flatMap
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
balance,
|
|
48
|
+
spendableBalance: balance,
|
|
49
|
+
operations: flatMap<TransactionWithId, InternetComputerOperation>(
|
|
50
|
+
txns,
|
|
51
|
+
mapTxToOps(accountId, address),
|
|
52
|
+
),
|
|
53
|
+
blockHeight: blockHeight.toNumber(),
|
|
54
|
+
operationsCount: (initialAccount?.operations.length ?? 0) + txns.length,
|
|
46
55
|
xpub: publicKey,
|
|
47
56
|
};
|
|
48
57
|
|
|
@@ -59,47 +68,65 @@ function reconciliatePublicKey(publicKey?: string, initialAccount?: Account): st
|
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
const mapTxToOps = (accountId: string, address: string, fee = ICP_FEES) => {
|
|
62
|
-
return (
|
|
63
|
-
|
|
64
|
-
): InternetComputerOperation[] => {
|
|
71
|
+
return (txInfo: TransactionWithId): InternetComputerOperation[] => {
|
|
72
|
+
const { transaction: txn } = txInfo;
|
|
65
73
|
const ops: InternetComputerOperation[] = [];
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const timeStamp =
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
74
|
+
|
|
75
|
+
if (txn.operation === undefined) {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if ("Transfer" in txn.operation === undefined) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const timeStamp = txn.timestamp[0]?.timestamp_nanos ?? Date.now();
|
|
84
|
+
let amount = BigNumber(0);
|
|
85
|
+
let fromAccount = "";
|
|
86
|
+
let toAccount = "";
|
|
87
|
+
let hash = "";
|
|
88
|
+
if ("Transfer" in txn.operation) {
|
|
89
|
+
amount = BigNumber(txn.operation.Transfer.amount.e8s.toString());
|
|
90
|
+
fromAccount = txn.operation.Transfer.from;
|
|
91
|
+
toAccount = txn.operation.Transfer.to;
|
|
92
|
+
hash = hashTransaction({
|
|
93
|
+
from: fromAccount,
|
|
94
|
+
to: toAccount,
|
|
95
|
+
amount: txn.operation.Transfer.amount.e8s,
|
|
96
|
+
fee: txn.operation.Transfer.fee.e8s,
|
|
97
|
+
memo: txn.memo,
|
|
98
|
+
created_at_time: txn.created_at_time[0]?.timestamp_nanos ?? BigInt(0),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const blockHeight = Number(txInfo.id);
|
|
103
|
+
const blockHash = "";
|
|
104
|
+
|
|
105
|
+
const memo = txInfo.transaction.memo.toString();
|
|
86
106
|
|
|
87
107
|
const date = new Date(normalizeEpochTimestamp(timeStamp.toString()));
|
|
88
108
|
const value = amount.abs();
|
|
89
109
|
const feeToUse = BigNumber(fee);
|
|
90
110
|
|
|
91
|
-
const isSending =
|
|
92
|
-
const isReceiving =
|
|
111
|
+
const isSending = address === fromAccount;
|
|
112
|
+
const isReceiving = address === toAccount;
|
|
113
|
+
|
|
114
|
+
let type: OperationType;
|
|
115
|
+
if (isSending) {
|
|
116
|
+
type = "OUT";
|
|
117
|
+
} else {
|
|
118
|
+
type = "IN";
|
|
119
|
+
}
|
|
93
120
|
|
|
94
121
|
if (isSending) {
|
|
95
122
|
ops.push({
|
|
96
|
-
id: encodeOperationId(accountId, hash,
|
|
123
|
+
id: encodeOperationId(accountId, hash, type),
|
|
97
124
|
hash,
|
|
98
|
-
type
|
|
125
|
+
type,
|
|
99
126
|
value: value.plus(feeToUse),
|
|
100
127
|
fee: feeToUse,
|
|
101
128
|
blockHeight,
|
|
102
|
-
blockHash
|
|
129
|
+
blockHash,
|
|
103
130
|
accountId,
|
|
104
131
|
senders: [fromAccount],
|
|
105
132
|
recipients: [toAccount],
|
|
@@ -112,13 +139,13 @@ const mapTxToOps = (accountId: string, address: string, fee = ICP_FEES) => {
|
|
|
112
139
|
|
|
113
140
|
if (isReceiving) {
|
|
114
141
|
ops.push({
|
|
115
|
-
id: encodeOperationId(accountId, hash,
|
|
142
|
+
id: encodeOperationId(accountId, hash, type),
|
|
116
143
|
hash,
|
|
117
|
-
type
|
|
144
|
+
type,
|
|
118
145
|
value,
|
|
119
146
|
fee: feeToUse,
|
|
120
147
|
blockHeight,
|
|
121
|
-
blockHash
|
|
148
|
+
blockHash,
|
|
122
149
|
accountId,
|
|
123
150
|
senders: [fromAccount],
|
|
124
151
|
recipients: [toAccount],
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import { log } from "@ledgerhq/logs";
|
|
2
1
|
import { Account } from "@ledgerhq/types-live";
|
|
3
|
-
import BigNumber from "bignumber.js";
|
|
4
|
-
import { MAX_MEMO_VALUE } from "../../consts";
|
|
5
|
-
import { fetchBalances } from "../../api";
|
|
6
2
|
|
|
7
3
|
export const getAddress = (
|
|
8
4
|
a: Account,
|
|
@@ -10,24 +6,3 @@ export const getAddress = (
|
|
|
10
6
|
address: string;
|
|
11
7
|
derivationPath: string;
|
|
12
8
|
} => ({ address: a.freshAddress, derivationPath: a.freshAddressPath });
|
|
13
|
-
|
|
14
|
-
export async function validateAddress(address: string): Promise<{ isValid: boolean }> {
|
|
15
|
-
try {
|
|
16
|
-
const res = await fetchBalances(address);
|
|
17
|
-
if (!res.balances) throw Error(res.details?.error_message);
|
|
18
|
-
return { isValid: true };
|
|
19
|
-
} catch (e: any) {
|
|
20
|
-
log("error", e.message ?? "Failed to validate address");
|
|
21
|
-
return { isValid: false };
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function validateMemo(memo?: string): { isValid: boolean } {
|
|
26
|
-
const res = BigNumber(memo ?? 0);
|
|
27
|
-
|
|
28
|
-
if (res.isNaN() || res.lt(0) || res.gt(BigNumber(MAX_MEMO_VALUE))) {
|
|
29
|
-
return { isValid: false };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return { isValid: true };
|
|
33
|
-
}
|
package/src/bridge/broadcast.ts
CHANGED
|
@@ -1,15 +1,40 @@
|
|
|
1
1
|
import { AccountBridge } from "@ledgerhq/types-live";
|
|
2
|
-
import { broadcastTxn } from "
|
|
2
|
+
import { broadcastTxn } from "../api";
|
|
3
3
|
import { Transaction } from "../types";
|
|
4
|
+
import { log } from "@ledgerhq/logs";
|
|
5
|
+
import invariant from "invariant";
|
|
6
|
+
import { MAINNET_LEDGER_CANISTER_ID } from "../consts";
|
|
4
7
|
|
|
8
|
+
// Interface to structure raw data for broadcasting transactions
|
|
9
|
+
interface BroadcastRawData {
|
|
10
|
+
encodedSignedCallBlob: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Type guard to validate rawData shape
|
|
14
|
+
function isBroadcastRawData(data: unknown): data is BroadcastRawData {
|
|
15
|
+
return (
|
|
16
|
+
typeof data === "object" &&
|
|
17
|
+
data !== null &&
|
|
18
|
+
"encodedSignedCallBlob" in data &&
|
|
19
|
+
typeof (data as any).encodedSignedCallBlob === "string"
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Main broadcast function for handling Internet Computer transactions
|
|
5
24
|
export const broadcast: AccountBridge<Transaction>["broadcast"] = async ({
|
|
6
|
-
signedOperation: {
|
|
25
|
+
signedOperation: { operation, rawData },
|
|
7
26
|
}) => {
|
|
8
|
-
|
|
27
|
+
log("debug", "[broadcast] Internet Computer transaction broadcast initiated");
|
|
9
28
|
|
|
10
|
-
|
|
29
|
+
// Validate rawData with type guard
|
|
30
|
+
invariant(isBroadcastRawData(rawData), "[ICP](broadcast) Invalid rawData format");
|
|
31
|
+
invariant(operation.extra, "[ICP](broadcast) Missing operation extra");
|
|
11
32
|
|
|
12
|
-
|
|
33
|
+
await broadcastTxn(
|
|
34
|
+
Buffer.from(rawData.encodedSignedCallBlob, "hex"),
|
|
35
|
+
MAINNET_LEDGER_CANISTER_ID,
|
|
36
|
+
"call",
|
|
37
|
+
);
|
|
13
38
|
|
|
14
|
-
return
|
|
39
|
+
return operation;
|
|
15
40
|
};
|
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
} from "@ledgerhq/errors";
|
|
8
8
|
import BigNumber from "bignumber.js";
|
|
9
9
|
import { AccountBridge } from "@ledgerhq/types-live";
|
|
10
|
-
import { getAddress
|
|
10
|
+
import { getAddress } from "./bridgeHelpers/addresses";
|
|
11
|
+
import { validateAddress, validateMemo } from "@zondax/ledger-live-icp/utils";
|
|
11
12
|
import { Transaction, TransactionStatus } from "../types";
|
|
12
13
|
import { InvalidMemoICP } from "../errors";
|
|
13
14
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AccountBridge } from "@ledgerhq/types-live";
|
|
2
2
|
import { Transaction } from "../types";
|
|
3
|
-
import { getAddress
|
|
3
|
+
import { getAddress } from "./bridgeHelpers/addresses";
|
|
4
|
+
import { validateAddress } from "@zondax/ledger-live-icp";
|
|
4
5
|
|
|
5
6
|
export const prepareTransaction: AccountBridge<Transaction>["prepareTransaction"] = async (
|
|
6
7
|
account,
|
|
@@ -1,53 +1,103 @@
|
|
|
1
1
|
import { Observable } from "rxjs";
|
|
2
|
-
import { Account, AccountBridge } from "@ledgerhq/types-live";
|
|
2
|
+
import { Account, AccountBridge, DeviceId } from "@ledgerhq/types-live";
|
|
3
3
|
import { getAddress } from "./bridgeHelpers/addresses";
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from "
|
|
5
|
+
UnsignedTransaction,
|
|
6
|
+
createUnsignedSendTransaction,
|
|
7
|
+
hashTransaction,
|
|
8
|
+
pubkeyToDer,
|
|
9
|
+
} from "@zondax/ledger-live-icp/utils";
|
|
10
|
+
import { Cbor } from "@zondax/ledger-live-icp/agent";
|
|
10
11
|
import { buildOptimisticOperation } from "./buildOptimisticOperation";
|
|
11
12
|
import { Transaction } from "../types";
|
|
12
13
|
import { SignerContext } from "@ledgerhq/coin-framework/signer";
|
|
13
14
|
import { ICPSigner } from "../types";
|
|
14
15
|
import { getPath } from "../common-logic";
|
|
16
|
+
import { log } from "@ledgerhq/logs";
|
|
17
|
+
import invariant from "invariant";
|
|
18
|
+
|
|
19
|
+
const signICPTransaction = async (
|
|
20
|
+
unsignedTxn: UnsignedTransaction,
|
|
21
|
+
derivationPath: string,
|
|
22
|
+
signerContext: SignerContext<ICPSigner>,
|
|
23
|
+
account: Account,
|
|
24
|
+
deviceId: DeviceId,
|
|
25
|
+
) => {
|
|
26
|
+
const blob = Cbor.encode({ content: unsignedTxn });
|
|
27
|
+
log("debug", "[signICPTransaction] blob", Buffer.from(blob).toString("hex"));
|
|
28
|
+
const signatures = await signerContext(deviceId, signer =>
|
|
29
|
+
signer.sign(derivationPath, Buffer.from(blob)),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
invariant(signatures.signatureRS, "[ICP](signICPTransaction) Signature not found");
|
|
33
|
+
invariant(account.xpub, "[ICP](signICPTransaction) Account xpub is required");
|
|
34
|
+
return {
|
|
35
|
+
signature: Buffer.from(signatures.signatureRS).toString("hex"),
|
|
36
|
+
callBody: {
|
|
37
|
+
content: unsignedTxn,
|
|
38
|
+
sender_pubkey: pubkeyToDer(account.xpub),
|
|
39
|
+
sender_sig: signatures.signatureRS,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
};
|
|
15
43
|
|
|
16
44
|
export const buildSignOperation =
|
|
17
|
-
(signerContext: SignerContext<ICPSigner>): AccountBridge<Transaction
|
|
45
|
+
(signerContext: SignerContext<ICPSigner>): AccountBridge<Transaction>["signOperation"] =>
|
|
18
46
|
({ account, transaction, deviceId }) =>
|
|
19
47
|
new Observable(o => {
|
|
20
48
|
async function main() {
|
|
49
|
+
log("debug", "[signOperation] icp start fn");
|
|
50
|
+
log("debug", "[signOperation] transaction", transaction);
|
|
51
|
+
|
|
21
52
|
const { xpub } = account;
|
|
53
|
+
invariant(xpub, "[ICP](signOperation) Account xpub is required");
|
|
54
|
+
|
|
22
55
|
const { derivationPath } = getAddress(account);
|
|
23
|
-
|
|
56
|
+
|
|
57
|
+
const { unsignedTransaction, transferRawRequest } = createUnsignedSendTransaction(
|
|
58
|
+
transaction,
|
|
59
|
+
xpub,
|
|
60
|
+
);
|
|
24
61
|
|
|
25
62
|
o.next({
|
|
26
63
|
type: "device-signature-requested",
|
|
27
64
|
});
|
|
28
65
|
|
|
29
|
-
|
|
66
|
+
let signature: string = "";
|
|
67
|
+
let encodedSignedCallBlob: string = "";
|
|
68
|
+
const res = await signICPTransaction(
|
|
69
|
+
unsignedTransaction,
|
|
70
|
+
getPath(derivationPath),
|
|
30
71
|
signerContext,
|
|
72
|
+
account,
|
|
31
73
|
deviceId,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
});
|
|
74
|
+
);
|
|
75
|
+
signature = res.signature;
|
|
76
|
+
encodedSignedCallBlob = Buffer.from(Cbor.encode(res.callBody)).toString("hex");
|
|
77
|
+
invariant(signature, "[ICP](signOperation) Signature not found");
|
|
37
78
|
|
|
38
79
|
o.next({
|
|
39
80
|
type: "device-signature-granted",
|
|
40
81
|
});
|
|
41
82
|
|
|
42
|
-
const
|
|
43
|
-
|
|
83
|
+
const hash = hashTransaction({
|
|
84
|
+
from: account.freshAddress,
|
|
85
|
+
to: transaction.recipient,
|
|
86
|
+
amount: transferRawRequest.amount.e8s,
|
|
87
|
+
fee: transferRawRequest.fee.e8s,
|
|
88
|
+
memo: transferRawRequest.memo,
|
|
89
|
+
created_at_time: transferRawRequest.created_at_time[0]["timestamp_nanos"],
|
|
90
|
+
});
|
|
44
91
|
|
|
92
|
+
const operation = await buildOptimisticOperation(account, transaction, hash);
|
|
45
93
|
o.next({
|
|
46
94
|
type: "signed",
|
|
47
95
|
signedOperation: {
|
|
48
96
|
operation,
|
|
49
|
-
signature
|
|
50
|
-
|
|
97
|
+
signature,
|
|
98
|
+
rawData: {
|
|
99
|
+
encodedSignedCallBlob,
|
|
100
|
+
},
|
|
51
101
|
},
|
|
52
102
|
});
|
|
53
103
|
}
|
package/src/consts.ts
CHANGED
|
@@ -9,3 +9,13 @@ export const ICP_FEES = 1e4;
|
|
|
9
9
|
|
|
10
10
|
// Max Memo value on ICP network
|
|
11
11
|
export const MAX_MEMO_VALUE = Number.MAX_SAFE_INTEGER;
|
|
12
|
+
|
|
13
|
+
// API limits
|
|
14
|
+
export const FETCH_TXNS_LIMIT = 100;
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
MAINNET_GOVERNANCE_CANISTER_ID,
|
|
18
|
+
MAINNET_LEDGER_CANISTER_ID,
|
|
19
|
+
MAINNET_INDEX_CANISTER_ID,
|
|
20
|
+
ICP_NETWORK_URL,
|
|
21
|
+
} from "@zondax/ledger-live-icp/neurons";
|
package/src/hw-signMessage.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { log } from "@ledgerhq/logs";
|
|
2
2
|
import { getBufferFromString } from "./common-logic/utils";
|
|
3
|
-
import { ICP_SEND_TXN_TYPE } from "./consts";
|
|
4
3
|
import { SignerContext } from "@ledgerhq/coin-framework/signer";
|
|
5
4
|
import { ICPSigner } from "./types";
|
|
6
5
|
import { Account, AnyMessage } from "@ledgerhq/types-live";
|
|
@@ -19,11 +18,7 @@ export const signMessage =
|
|
|
19
18
|
if (typeof message !== "string") throw new Error("Message must be a string");
|
|
20
19
|
|
|
21
20
|
const { r } = await signerContext(deviceId, async signer => {
|
|
22
|
-
const r = await signer.sign(
|
|
23
|
-
account.freshAddressPath,
|
|
24
|
-
getBufferFromString(message),
|
|
25
|
-
ICP_SEND_TXN_TYPE,
|
|
26
|
-
);
|
|
21
|
+
const r = await signer.sign(account.freshAddressPath, getBufferFromString(message));
|
|
27
22
|
return { r };
|
|
28
23
|
});
|
|
29
24
|
|
package/src/types/signer.ts
CHANGED
|
@@ -18,5 +18,5 @@ export type ICPGetAddrResponse = {
|
|
|
18
18
|
export interface ICPSigner {
|
|
19
19
|
showAddressAndPubKey(path: string): Promise<ICPGetAddrResponse>;
|
|
20
20
|
getAddressAndPubKey(path: string): Promise<ICPGetAddrResponse>;
|
|
21
|
-
sign(path: string, message: Buffer
|
|
21
|
+
sign(path: string, message: Buffer): Promise<ICPSignature>;
|
|
22
22
|
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { Account } from "@ledgerhq/types-live";
|
|
2
|
-
import { Transaction } from "../../../types";
|
|
3
|
-
import { ICPRosettaConstructionPayloadsResponse } from "./types";
|
|
4
|
-
import { SignerContext } from "@ledgerhq/coin-framework/signer";
|
|
5
|
-
import { ICPSigner } from "../../../types";
|
|
6
|
-
export declare const getUnsignedTransaction: (transaction: Transaction, account: Account) => Promise<{
|
|
7
|
-
unsignedTxn: string;
|
|
8
|
-
payloads: ICPRosettaConstructionPayloadsResponse["payloads"];
|
|
9
|
-
}>;
|
|
10
|
-
export declare const signICPTransaction: ({ signerContext, deviceId, unsignedTxn, path, payloads, pubkey, }: {
|
|
11
|
-
signerContext: SignerContext<ICPSigner>;
|
|
12
|
-
deviceId: string;
|
|
13
|
-
unsignedTxn: string;
|
|
14
|
-
path: string;
|
|
15
|
-
payloads: ICPRosettaConstructionPayloadsResponse["payloads"];
|
|
16
|
-
pubkey: string;
|
|
17
|
-
}) => Promise<{
|
|
18
|
-
signatures: {
|
|
19
|
-
txnSig: string;
|
|
20
|
-
readSig: string;
|
|
21
|
-
};
|
|
22
|
-
signedTxn: string;
|
|
23
|
-
}>;
|
|
24
|
-
export declare const getTxnMetadata: (signedTxn: string) => Promise<{
|
|
25
|
-
hash: string;
|
|
26
|
-
}>;
|
|
27
|
-
export declare const getTxnExpirationDate: (_unsignedTxn: string) => Date;
|
|
28
|
-
export declare const broadcastTxn: (signedTxn: string) => Promise<void>;
|
|
29
|
-
export declare const deriveAddressFromPubkey: (pubkey: string) => Promise<string>;
|
|
30
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/bridge/bridgeHelpers/icpRosetta/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,OAAO,EAML,sCAAsC,EAKvC,MAAM,SAAS,CAAC;AAMjB,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,eAAO,MAAM,sBAAsB,gBACpB,WAAW,WACf,OAAO;iBAEH,MAAM;cACT,sCAAsC,CAAC,UAAU,CAAC;EAwB7D,CAAC;AAEF,eAAO,MAAM,kBAAkB;mBAQd,cAAc,SAAS,CAAC;cAC7B,MAAM;iBACH,MAAM;UACb,MAAM;cACF,sCAAsC,CAAC,UAAU,CAAC;YACpD,MAAM;;gBAEF;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;eACpC,MAAM;EA8ClB,CAAC;AAEF,eAAO,MAAM,cAAc,cAAqB,MAAM;UAAmB,MAAM;EAS9E,CAAC;AAEF,eAAO,MAAM,oBAAoB,iBAAkB,MAAM,KAAG,IAE3D,CAAC;AAEF,eAAO,MAAM,YAAY,cAAqB,MAAM,kBAKnD,CAAC;AAEF,eAAO,MAAM,uBAAuB,WAAkB,MAAM,oBAa3D,CAAC"}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.deriveAddressFromPubkey = exports.broadcastTxn = exports.getTxnExpirationDate = exports.getTxnMetadata = exports.signICPTransaction = exports.getUnsignedTransaction = void 0;
|
|
7
|
-
const api_1 = require("../../../api");
|
|
8
|
-
const utils_1 = require("./utils");
|
|
9
|
-
const agent_1 = require("@dfinity/agent");
|
|
10
|
-
const principal_1 = require("@dfinity/principal");
|
|
11
|
-
const bignumber_js_1 = __importDefault(require("bignumber.js"));
|
|
12
|
-
const consts_1 = require("../../../consts");
|
|
13
|
-
const getUnsignedTransaction = async (transaction, account) => {
|
|
14
|
-
const ops = (0, utils_1.generateOperations)(transaction, account);
|
|
15
|
-
const pubkeys = [
|
|
16
|
-
{
|
|
17
|
-
hex_bytes: account.xpub ?? "",
|
|
18
|
-
curve_type: "secp256k1",
|
|
19
|
-
},
|
|
20
|
-
];
|
|
21
|
-
const reqOpts = {
|
|
22
|
-
...(0, api_1.getICPRosettaNetworkIdentifier)(),
|
|
23
|
-
operations: ops,
|
|
24
|
-
public_keys: pubkeys,
|
|
25
|
-
metadata: {
|
|
26
|
-
memo: parseInt(transaction.memo ?? "0"),
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
const { payloads, unsigned_transaction } = await (0, api_1.constructionInvoke)(reqOpts, "payloads");
|
|
30
|
-
return { unsignedTxn: unsigned_transaction, payloads };
|
|
31
|
-
};
|
|
32
|
-
exports.getUnsignedTransaction = getUnsignedTransaction;
|
|
33
|
-
const signICPTransaction = async ({ signerContext, deviceId, unsignedTxn, path, payloads, pubkey, }) => {
|
|
34
|
-
const decodedTxn = agent_1.Cbor.decode(Buffer.from(unsignedTxn, "hex"));
|
|
35
|
-
const txnReqFromCbor = decodedTxn.updates[0][1];
|
|
36
|
-
const expiry = new utils_1.ingressExpiry((0, bignumber_js_1.default)(decodedTxn.ingress_expiries[0].toString()));
|
|
37
|
-
const submitReq = {
|
|
38
|
-
request_type: "call",
|
|
39
|
-
canister_id: principal_1.Principal.fromUint8Array(txnReqFromCbor.canister_id),
|
|
40
|
-
method_name: txnReqFromCbor.method_name,
|
|
41
|
-
arg: txnReqFromCbor.arg,
|
|
42
|
-
sender: principal_1.Principal.fromUint8Array(txnReqFromCbor.sender),
|
|
43
|
-
ingress_expiry: expiry,
|
|
44
|
-
};
|
|
45
|
-
const txnBlobToSign = agent_1.Cbor.encode({
|
|
46
|
-
content: submitReq,
|
|
47
|
-
});
|
|
48
|
-
const { r } = await signerContext(deviceId, async (signer) => {
|
|
49
|
-
const r = await signer.sign(path, Buffer.from(txnBlobToSign), consts_1.ICP_SEND_TXN_TYPE);
|
|
50
|
-
return { r };
|
|
51
|
-
});
|
|
52
|
-
const result = {
|
|
53
|
-
signatures: {
|
|
54
|
-
readSig: "",
|
|
55
|
-
txnSig: Buffer.from(r.signatureRS ?? "").toString("hex"),
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
const signaturesPayload = (0, utils_1.generateSignaturesPayload)(result.signatures, payloads, pubkey);
|
|
59
|
-
const { signed_transaction: signedTxn } = await (0, api_1.constructionInvoke)({
|
|
60
|
-
...(0, api_1.getICPRosettaNetworkIdentifier)(),
|
|
61
|
-
signatures: signaturesPayload,
|
|
62
|
-
unsigned_transaction: unsignedTxn,
|
|
63
|
-
}, "combine");
|
|
64
|
-
return { ...result, signedTxn };
|
|
65
|
-
};
|
|
66
|
-
exports.signICPTransaction = signICPTransaction;
|
|
67
|
-
const getTxnMetadata = async (signedTxn) => {
|
|
68
|
-
const { transaction_identifier: { hash }, } = await (0, api_1.constructionInvoke)({ ...(0, api_1.getICPRosettaNetworkIdentifier)(), signed_transaction: signedTxn }, "hash");
|
|
69
|
-
return { hash };
|
|
70
|
-
};
|
|
71
|
-
exports.getTxnMetadata = getTxnMetadata;
|
|
72
|
-
const getTxnExpirationDate = (_unsignedTxn) => {
|
|
73
|
-
return new Date();
|
|
74
|
-
};
|
|
75
|
-
exports.getTxnExpirationDate = getTxnExpirationDate;
|
|
76
|
-
const broadcastTxn = async (signedTxn) => {
|
|
77
|
-
await (0, api_1.constructionInvoke)({ ...(0, api_1.getICPRosettaNetworkIdentifier)(), signed_transaction: signedTxn }, "submit");
|
|
78
|
-
};
|
|
79
|
-
exports.broadcastTxn = broadcastTxn;
|
|
80
|
-
const deriveAddressFromPubkey = async (pubkey) => {
|
|
81
|
-
const res = await (0, api_1.constructionInvoke)({
|
|
82
|
-
...(0, api_1.getICPRosettaNetworkIdentifier)(),
|
|
83
|
-
public_key: { curve_type: "secp256k1", hex_bytes: pubkey },
|
|
84
|
-
}, "derive");
|
|
85
|
-
return res.account_identifier.address;
|
|
86
|
-
};
|
|
87
|
-
exports.deriveAddressFromPubkey = deriveAddressFromPubkey;
|
|
88
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/bridge/bridgeHelpers/icpRosetta/index.ts"],"names":[],"mappings":"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"}
|