Skip to content

Commit fae0c3d

Browse files
committed
Merge pull-request #1096
2 parents 3c9d5ff + 560338c commit fae0c3d

File tree

6 files changed

+115
-19
lines changed

6 files changed

+115
-19
lines changed

.changeset/lemon-breads-win.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@turnkey/core": patch
3+
---
4+
5+
Fixed legacy transactions not working in `signAndSendTransaction()` for **EVM connected wallet**. This does not affect Turnkey's embedded wallet flow, previously, connected wallet transactions were all formatted into EIP-1559 transactions, updated to respect legacy + future formats passed in.

examples/with-sdk-js/src/app/layout.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,15 @@ function RootLayout({ children }: RootLayoutProps) {
6262
],
6363
},
6464
},
65-
// walletConnect: {
66-
// projectId: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID!,
67-
// appMetadata: {
68-
// name: "Turnkey Wallet",
69-
// description: "A wallet for Turnkey",
70-
// url: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_URL!,
71-
// icons: ["/favicon.svg"],
72-
// },
73-
// },
65+
walletConnect: {
66+
projectId: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID!,
67+
appMetadata: {
68+
name: "Turnkey Wallet",
69+
description: "A wallet for Turnkey",
70+
url: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_URL!,
71+
icons: ["/favicon.svg"],
72+
},
73+
},
7474
},
7575
}}
7676
>

examples/with-sdk-js/src/app/page.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,8 +1624,9 @@ export default function AuthPage() {
16241624
value: parseEther("0.001"),
16251625
nonce: 0,
16261626
gasLimit: BigInt("21000"),
1627-
maxFeePerGas: BigInt("1000000000"),
1628-
maxPriorityFeePerGas: BigInt("1000000000"),
1627+
gasPrice: BigInt("100000000000000"),
1628+
// maxFeePerGas: BigInt("1000000000"),
1629+
// maxPriorityFeePerGas: BigInt("1000000000"),
16291630
chainId: 1,
16301631
};
16311632

packages/core/src/__types__/external-wallets.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,3 +218,27 @@ export interface ConnectedWallet extends v1Wallet {
218218

219219
/** @internal */
220220
export type Wallet = EmbeddedWallet | ConnectedWallet;
221+
222+
export type EvmTransactionParams = {
223+
from: `0x${string}`;
224+
to: `0x${string}`;
225+
value: `0x${string}`;
226+
gas: `0x${string}`;
227+
nonce: `0x${string}`;
228+
chainId: `0x${string}`;
229+
data: `0x${string}`;
230+
};
231+
232+
export type EIP1559TransactionParams = EvmTransactionParams & {
233+
maxFeePerGas: `0x${string}`;
234+
maxPriorityFeePerGas: `0x${string}`;
235+
};
236+
237+
export type LegacyEvmTransactionParams = EvmTransactionParams & {
238+
gasPrice: `0x${string}`;
239+
};
240+
241+
/** @internal */
242+
export type EthereumTxParams =
243+
| EIP1559TransactionParams
244+
| LegacyEvmTransactionParams;

packages/core/src/__wallet__/wallet-connect/base.ts

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {
1515
WalletConnectProvider,
1616
WalletConnectInterface,
1717
SwitchableChain,
18+
EvmTransactionParams,
19+
EthereumTxParams,
1820
} from "../../__types__";
1921
import type { WalletConnectClient } from "./client";
2022
import type { SessionTypes } from "@walletconnect/types";
@@ -320,20 +322,52 @@ export class WalletConnectWallet implements WalletConnectInterface {
320322
address,
321323
])) as string;
322324
case SignIntent.SignAndSendTransaction:
323-
const account = provider.connectedAddresses[0];
325+
const account = provider.connectedAddresses[0] as Hex;
326+
if (!account) throw new Error("no connected address");
324327
const tx = Transaction.from(payload);
325-
const txParams = {
328+
329+
const base: EvmTransactionParams = {
326330
from: account,
327331
to: tx.to?.toString() as Hex,
328332
value: toHex(tx.value),
329333
gas: toHex(tx.gasLimit),
330-
maxFeePerGas: toHex(tx.maxFeePerGas ?? 0n),
331-
maxPriorityFeePerGas: toHex(tx.maxPriorityFeePerGas ?? 0n),
332334
nonce: toHex(tx.nonce),
333335
chainId: toHex(tx.chainId),
334336
data: (tx.data?.toString() as Hex) ?? "0x",
335337
};
336338

339+
// Some libs use undefined for legacy, so normalize
340+
const txType = (tx as any).type ?? 0;
341+
342+
let txParams: EthereumTxParams;
343+
344+
if (txType === undefined || txType === 0 || txType === 1) {
345+
// legacy or EIP-2930 (gasPrice-based)
346+
if (tx.gasPrice == null) {
347+
throw new Error(
348+
"Legacy or EIP-2930 transaction missing gasPrice",
349+
);
350+
}
351+
352+
txParams = {
353+
...base,
354+
gasPrice: toHex(tx.gasPrice),
355+
};
356+
} else {
357+
// EIP-1559 or future fee-market types
358+
if (tx.maxFeePerGas == null || tx.maxPriorityFeePerGas == null) {
359+
throw new Error(
360+
"EIP-1559-style transaction missing maxFeePerGas or maxPriorityFeePerGas",
361+
);
362+
}
363+
364+
txParams = {
365+
...base,
366+
maxFeePerGas: toHex(tx.maxFeePerGas),
367+
maxPriorityFeePerGas: toHex(tx.maxPriorityFeePerGas),
368+
};
369+
}
370+
337371
return (await this.client.request(
338372
this.ethChain,
339373
"eth_sendTransaction",

packages/core/src/__wallet__/web/native/ethereum.ts

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import {
99
import { Transaction } from "ethers";
1010
import { compressRawPublicKey } from "@turnkey/crypto";
1111
import {
12+
EvmTransactionParams,
1213
Chain,
14+
EthereumTxParams,
1315
EthereumWalletInterface,
1416
SignIntent,
1517
SwitchableChain,
@@ -279,24 +281,54 @@ export class EthereumWallet extends BaseEthereumWallet {
279281
params: [payload as Hex, account],
280282
});
281283

282-
case SignIntent.SignAndSendTransaction:
284+
case SignIntent.SignAndSendTransaction: {
283285
const tx = Transaction.from(payload);
284-
const txParams = {
286+
287+
const base: EvmTransactionParams = {
285288
from: account,
286289
to: tx.to?.toString() as Hex,
287290
value: toHex(tx.value),
288291
gas: toHex(tx.gasLimit),
289-
maxFeePerGas: toHex(tx.maxFeePerGas ?? 0n),
290-
maxPriorityFeePerGas: toHex(tx.maxPriorityFeePerGas ?? 0n),
291292
nonce: toHex(tx.nonce),
292293
chainId: toHex(tx.chainId),
293294
data: (tx.data?.toString() as Hex) ?? "0x",
294295
};
295296

297+
// Some libs use undefined for legacy, so normalize
298+
const txType = (tx as any).type ?? 0;
299+
300+
let txParams: EthereumTxParams;
301+
302+
if (txType === undefined || txType === 0 || txType === 1) {
303+
// legacy or EIP-2930 (gasPrice-based)
304+
if (tx.gasPrice == null) {
305+
throw new Error("Legacy or EIP-2930 transaction missing gasPrice");
306+
}
307+
308+
txParams = {
309+
...base,
310+
gasPrice: toHex(tx.gasPrice),
311+
};
312+
} else {
313+
// EIP-1559 or future fee-market types
314+
if (tx.maxFeePerGas == null || tx.maxPriorityFeePerGas == null) {
315+
throw new Error(
316+
"EIP-1559-style transaction missing maxFeePerGas or maxPriorityFeePerGas",
317+
);
318+
}
319+
320+
txParams = {
321+
...base,
322+
maxFeePerGas: toHex(tx.maxFeePerGas),
323+
maxPriorityFeePerGas: toHex(tx.maxPriorityFeePerGas),
324+
};
325+
}
326+
296327
return await selectedProvider.request({
297328
method: "eth_sendTransaction",
298329
params: [txParams],
299330
});
331+
}
300332

301333
default:
302334
throw new Error(`Unsupported sign intent: ${intent}`);

0 commit comments

Comments
 (0)