@swapkit/wallet-hardware 4.10.0 → 4.11.0

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.
@@ -0,0 +1,127 @@
1
+ import type Transport from "@ledgerhq/hw-transport";
2
+
3
+ const LEDGER_CAL_SERVICE_URL = "https://crypto-assets-service.api.ledger.com/v1";
4
+
5
+ const LEDGER_DEVICE_TO_CAL_MODEL: Record<string, string> = {
6
+ apex: "apexp",
7
+ europa: "flex",
8
+ nanoS: "nanos",
9
+ nanoSP: "nanosp",
10
+ nanoX: "nanox",
11
+ stax: "stax",
12
+ };
13
+
14
+ const APDU_CLA = 0xe0;
15
+ const APDU_CLA_PKI = 0xb0;
16
+ const APDU_INS_LOAD_CERTIFICATE = 0x06;
17
+ const LEDGER_KEY_USAGE_NETWORK = 0x0c;
18
+ const APDU_INS_PROVIDE_NETWORK_INFORMATION = 0x30;
19
+ const APDU_P1_FIRST_CHUNK = 0x01;
20
+ const APDU_P1_FOLLOWING_CHUNK = 0x00;
21
+ const APDU_P2_NETWORK_CONFIG = 0x00;
22
+ const APDU_P2_NETWORK_ICON = 0x01;
23
+ const APDU_MAX_CHUNK_SIZE = 0xff;
24
+ const TLV_TAG_DER_SIGNATURE = 0x15;
25
+
26
+ export type LedgerNetworkDescriptor = { data: string; icon?: string; signature: string };
27
+
28
+ type CalNetwork = {
29
+ chain_id: number;
30
+ descriptors?: Record<string, { data?: string; signatures?: { prod?: string } }>;
31
+ icons?: Record<string, string>;
32
+ };
33
+
34
+ export async function fetchLedgerNetworkCertificate(
35
+ deviceModelId: string,
36
+ fetchFn: typeof fetch = fetch,
37
+ ): Promise<string | null> {
38
+ const calModel = LEDGER_DEVICE_TO_CAL_MODEL[deviceModelId];
39
+ if (!calModel) return null;
40
+
41
+ const url = `${LEDGER_CAL_SERVICE_URL}/certificates?output=descriptor&public_key_usage=network&target_device=${calModel}`;
42
+ const response = await fetchFn(url);
43
+ if (!response.ok) return null;
44
+
45
+ const certificates = (await response.json()) as Array<{
46
+ descriptor?: { data?: string; signatures?: { prod?: string } };
47
+ }>;
48
+ const descriptor = certificates[0]?.descriptor;
49
+ const signature = descriptor?.signatures?.prod;
50
+
51
+ if (!(descriptor?.data && signature)) return null;
52
+
53
+ return `${descriptor.data}${appendSignatureTlv(signature)}`;
54
+ }
55
+
56
+ export async function provideLedgerCertificate(transport: Transport, payloadHex: string) {
57
+ await transport.send(
58
+ APDU_CLA_PKI,
59
+ APDU_INS_LOAD_CERTIFICATE,
60
+ LEDGER_KEY_USAGE_NETWORK,
61
+ 0x00,
62
+ Buffer.from(payloadHex, "hex"),
63
+ );
64
+ }
65
+
66
+ export async function fetchLedgerNetworkDescriptor(
67
+ chainId: number,
68
+ deviceModelId: string,
69
+ fetchFn: typeof fetch = fetch,
70
+ ): Promise<LedgerNetworkDescriptor | null> {
71
+ const calModel = LEDGER_DEVICE_TO_CAL_MODEL[deviceModelId];
72
+ if (!calModel) return null;
73
+
74
+ const url = `${LEDGER_CAL_SERVICE_URL}/networks?chain_id=${chainId}&output=chain_id,descriptors,icons`;
75
+ const response = await fetchFn(url);
76
+ if (!response.ok) return null;
77
+
78
+ const networks = (await response.json()) as CalNetwork[];
79
+ const network = networks.find((entry) => entry.chain_id === chainId);
80
+ const descriptor = network?.descriptors?.[calModel];
81
+ const signature = descriptor?.signatures?.prod;
82
+
83
+ if (!(descriptor?.data && signature)) return null;
84
+
85
+ return { data: descriptor.data, icon: network?.icons?.[calModel], signature };
86
+ }
87
+
88
+ function appendSignatureTlv(signature: string) {
89
+ return encodeTlv(TLV_TAG_DER_SIGNATURE, Buffer.from(signature, "hex")).toString("hex");
90
+ }
91
+
92
+ function encodeTlv(tag: number, value: Buffer) {
93
+ return Buffer.concat([Buffer.from([tag, value.length]), value]);
94
+ }
95
+
96
+ export function encodeNetworkInfoPayload({ data, signature }: LedgerNetworkDescriptor) {
97
+ const tlv = Buffer.concat([
98
+ Buffer.from(data, "hex"),
99
+ encodeTlv(TLV_TAG_DER_SIGNATURE, Buffer.from(signature, "hex")),
100
+ ]);
101
+ const length = Buffer.alloc(2);
102
+ length.writeUInt16BE(tlv.length, 0);
103
+ return Buffer.concat([length, tlv]);
104
+ }
105
+
106
+ async function sendChunked(transport: Transport, p2: number, payload: Buffer) {
107
+ for (let offset = 0; offset < payload.length; offset += APDU_MAX_CHUNK_SIZE) {
108
+ const p1 = offset === 0 ? APDU_P1_FIRST_CHUNK : APDU_P1_FOLLOWING_CHUNK;
109
+ const chunk = payload.subarray(offset, offset + APDU_MAX_CHUNK_SIZE);
110
+ await transport.send(APDU_CLA, APDU_INS_PROVIDE_NETWORK_INFORMATION, p1, p2, chunk);
111
+ }
112
+ }
113
+
114
+ export async function provideLedgerNetworkInformation(transport: Transport, descriptor: LedgerNetworkDescriptor) {
115
+ const payload = encodeNetworkInfoPayload(descriptor);
116
+ await sendChunked(transport, APDU_P2_NETWORK_CONFIG, payload);
117
+
118
+ if (!descriptor.icon) return { iconAccepted: false };
119
+
120
+ try {
121
+ await sendChunked(transport, APDU_P2_NETWORK_ICON, Buffer.from(descriptor.icon, "hex"));
122
+ return { iconAccepted: true };
123
+ } catch {
124
+ await sendChunked(transport, APDU_P2_NETWORK_CONFIG, payload);
125
+ return { iconAccepted: false };
126
+ }
127
+ }
@@ -123,14 +123,13 @@ export async function getEVMSigner({ chain, derivationPath, provider }: TrezorEV
123
123
  if (!to) throw new SwapKitError({ errorKey: "wallet_missing_params", info: { to } });
124
124
  if (!gasLimit) throw new SwapKitError({ errorKey: "wallet_missing_params", info: { gasLimit } });
125
125
 
126
- const isEIP1559 = maxFeePerGas && maxPriorityFeePerGas;
126
+ // Check presence, not truthiness: a zero-wei tip is legitimate on chains with a negligible
127
+ // priority fee, such as Arc, where it used to be read as missing fee data.
128
+ const isEIP1559 = maxFeePerGas != null || maxPriorityFeePerGas != null;
127
129
 
128
130
  if (isEIP1559 && !maxFeePerGas) {
129
131
  throw new SwapKitError({ errorKey: "wallet_missing_params", info: { maxFeePerGas } });
130
132
  }
131
- if (isEIP1559 && !maxPriorityFeePerGas) {
132
- throw new SwapKitError({ errorKey: "wallet_missing_params", info: { maxPriorityFeePerGas } });
133
- }
134
133
  if (!(isEIP1559 || gasPrice)) {
135
134
  throw new SwapKitError({ errorKey: "wallet_missing_params", info: { gasPrice } });
136
135
  }
@@ -498,6 +498,7 @@ async function getTrezorWallet<T extends Chain>({
498
498
  }) {
499
499
  switch (chain) {
500
500
  case Chain.Arbitrum:
501
+ case Chain.Arc:
501
502
  case Chain.Aurora:
502
503
  case Chain.Avalanche:
503
504
  case Chain.Base:
@@ -1358,6 +1359,7 @@ export const trezorWallet = createWallet({
1358
1359
  },
1359
1360
  directSigningSupport: {
1360
1361
  [Chain.Arbitrum]: true,
1362
+ [Chain.Arc]: true,
1361
1363
  [Chain.Aurora]: true,
1362
1364
  [Chain.Avalanche]: true,
1363
1365
  [Chain.Base]: true,
@@ -1380,6 +1382,7 @@ export const trezorWallet = createWallet({
1380
1382
  name: "connectTrezor",
1381
1383
  supportedChains: [
1382
1384
  Chain.Arbitrum,
1385
+ Chain.Arc,
1383
1386
  Chain.Aurora,
1384
1387
  Chain.Avalanche,
1385
1388
  Chain.Base,
@@ -1,4 +0,0 @@
1
- import{d as Q}from"./chunk-n05bv2n5.js";import{ChainToChainId as b,derivationPathToString as $,SwapKitError as B,WalletOption as K}from"@swapkit/helpers";async function G({chain:_,derivationPath:f,provider:C}){let{AbstractSigner:H}=await import("ethers");class A extends H{address;chain;derivationPath;provider;constructor({chain:j,derivationPath:q,provider:J}){super(J);this.address="",this.chain=j,this.derivationPath=q,this.provider=J}getAddress=async()=>{if(!this.address){let q=await(await import("@trezor/connect-web")).default.ethereumGetAddress({path:$(this.derivationPath),showOnTrezor:!0});if(!q.success)throw new B({errorKey:"wallet_trezor_failed_to_get_address",info:{...q,chain:this.chain,derivationPath:this.derivationPath}});this.address=q.payload.address}return this.address};signMessage=async(j)=>{let J=await(await import("@trezor/connect-web")).default.ethereumSignMessage({message:j,path:$(this.derivationPath)});if(!J.success)throw new B({errorKey:"wallet_trezor_failed_to_sign_transaction",info:{...J,chain:this.chain,derivationPath:this.derivationPath,message:j}});return J.payload.signature};signTypedData=async(j,q,J,N)=>{let O=(await import("@trezor/connect-web")).default,{buildEIP712DomainType:V}=await import("@swapkit/toolboxes/evm"),{TypedDataEncoder:X}=await import("ethers"),{EIP712Domain:Y,...R}=q,W=N??X.from(R).primaryType,M=V(j),D={domain:j,message:J,primaryType:W,types:{EIP712Domain:M,...R}},U=await O.ethereumSignTypedData({data:D,metamask_v4_compat:!0,path:$(this.derivationPath)});if(!U.success)throw new B({errorKey:"wallet_trezor_failed_to_sign_transaction",info:{chain:this.chain,error:U.payload.error}});return U.payload.signature.startsWith("0x")?U.payload.signature:`0x${U.payload.signature}`};signTransaction=async({to:j,gasLimit:q,value:J,data:N,nonce:O,maxFeePerGas:V,maxPriorityFeePerGas:X,gasPrice:Y})=>{if(!j)throw new B({errorKey:"wallet_missing_params",info:{to:j}});if(!q)throw new B({errorKey:"wallet_missing_params",info:{gasLimit:q}});let R=V&&X;if(R&&!V)throw new B({errorKey:"wallet_missing_params",info:{maxFeePerGas:V}});if(R&&!X)throw new B({errorKey:"wallet_missing_params",info:{maxPriorityFeePerGas:X}});if(!(R||Y))throw new B({errorKey:"wallet_missing_params",info:{gasPrice:Y}});let W=(await import("@trezor/connect-web")).default,{toHexString:M}=await import("@swapkit/toolboxes/evm"),D=R?{maxFeePerGas:M(BigInt(V?.toString()||0)),maxPriorityFeePerGas:M(BigInt(X?.toString()||0))}:Y&&{gasPrice:M(BigInt(Y?.toString()||0))}||{gasPrice:"0x0"},U=M(BigInt(O||await this.provider.getTransactionCount(await this.getAddress(),"pending"))),I={chainId:Number.parseInt(b[this.chain],10),data:N?.toString()||"0x",gasLimit:M(BigInt(q?.toString()||0)),nonce:U,to:j.toString(),value:M(BigInt(J?.toString()||0)),...D},{success:L,payload:Z}=await W.ethereumSignTransaction({path:$(this.derivationPath),transaction:I});if(!L)throw new B({errorKey:"wallet_trezor_failed_to_sign_transaction",info:{...Z,chain:this.chain,derivationPath:this.derivationPath}});let k=Z.serializedTx?Z.serializedTx.startsWith("0x")?Z.serializedTx:`0x${Z.serializedTx}`:void 0;if(!k)throw new B({errorKey:"wallet_trezor_failed_to_sign_transaction",info:{chain:this.chain,derivationPath:this.derivationPath}});return k};connect=(j)=>{if(!j)throw new B({errorKey:"wallet_provider_not_found",info:{chain:this.chain,derivationPath:this.derivationPath,wallet:K.TREZOR}});return new A({chain:this.chain,derivationPath:this.derivationPath,provider:j})}}return new A({chain:_,derivationPath:f,provider:C})}export{G as getEVMSigner};
2
-
3
- //# debugId=654BE0775B0291CD64756E2164756E21
4
- //# sourceMappingURL=chunk-qs60k0yv.js.map