genlayer-js 0.18.7 → 0.18.9

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/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  localnet,
6
6
  studionet,
7
7
  testnetAsimov
8
- } from "./chunk-RW6PLN5W.js";
8
+ } from "./chunk-V3MYVW3P.js";
9
9
  import {
10
10
  CalldataAddress,
11
11
  isDecidedState,
@@ -743,10 +743,44 @@ var _sendTransaction = async ({
743
743
  data: encodedData,
744
744
  value
745
745
  });
746
- } catch (error) {
747
- console.warn("Gas estimation failed, using fallback value:", error);
746
+ } catch (err) {
747
+ console.error("Gas estimation failed, using default 200_000:", err);
748
748
  estimatedGas = 200000n;
749
749
  }
750
+ if (validatedSenderAccount?.type === "local") {
751
+ if (!validatedSenderAccount?.signTransaction) {
752
+ throw new Error("Account does not support signTransaction");
753
+ }
754
+ const gasPriceHex = await client.request({
755
+ method: "eth_gasPrice"
756
+ });
757
+ const transactionRequest2 = {
758
+ account: validatedSenderAccount,
759
+ to: client.chain.consensusMainContract?.address,
760
+ data: encodedData,
761
+ type: "legacy",
762
+ nonce: Number(nonce),
763
+ value,
764
+ gas: estimatedGas,
765
+ gasPrice: BigInt(gasPriceHex),
766
+ chainId: client.chain.id
767
+ };
768
+ const serializedTransaction = await validatedSenderAccount.signTransaction(transactionRequest2);
769
+ const txHash = await client.sendRawTransaction({ serializedTransaction });
770
+ const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
771
+ if (receipt.status === "reverted") {
772
+ throw new Error("Transaction reverted");
773
+ }
774
+ const newTxEvents = parseEventLogs({
775
+ abi: client.chain.consensusMainContract?.abi,
776
+ eventName: "NewTransaction",
777
+ logs: receipt.logs
778
+ });
779
+ if (newTxEvents.length === 0) {
780
+ throw new Error("Transaction not processed by consensus");
781
+ }
782
+ return newTxEvents[0].args["txId"];
783
+ }
750
784
  const transactionRequest = await client.prepareTransactionRequest({
751
785
  account: validatedSenderAccount,
752
786
  to: client.chain.consensusMainContract?.address,
@@ -756,37 +790,17 @@ var _sendTransaction = async ({
756
790
  value,
757
791
  gas: estimatedGas
758
792
  });
759
- if (validatedSenderAccount?.type !== "local") {
760
- const formattedRequest = {
761
- from: transactionRequest.from,
762
- to: transactionRequest.to,
763
- data: encodedData,
764
- value: transactionRequest.value ? `0x${transactionRequest.value.toString(16)}` : "0x0",
765
- gas: transactionRequest.gas ? `0x${transactionRequest.gas.toString(16)}` : "0x5208"
766
- };
767
- return await client.request({
768
- method: "eth_sendTransaction",
769
- params: [formattedRequest]
770
- });
771
- }
772
- if (!validatedSenderAccount?.signTransaction) {
773
- throw new Error("Account does not support signTransaction");
774
- }
775
- const serializedTransaction = await validatedSenderAccount.signTransaction(transactionRequest);
776
- const txHash = await client.sendRawTransaction({ serializedTransaction });
777
- const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
778
- if (receipt.status === "reverted") {
779
- throw new Error("Transaction reverted");
780
- }
781
- const newTxEvents = parseEventLogs({
782
- abi: client.chain.consensusMainContract?.abi,
783
- eventName: "NewTransaction",
784
- logs: receipt.logs
793
+ const formattedRequest = {
794
+ from: transactionRequest.from,
795
+ to: transactionRequest.to,
796
+ data: encodedData,
797
+ value: transactionRequest.value ? `0x${transactionRequest.value.toString(16)}` : "0x0",
798
+ gas: transactionRequest.gas ? `0x${transactionRequest.gas.toString(16)}` : "0x5208"
799
+ };
800
+ return await client.request({
801
+ method: "eth_sendTransaction",
802
+ params: [formattedRequest]
785
803
  });
786
- if (newTxEvents.length === 0) {
787
- throw new Error("Transaction not processed by consensus");
788
- }
789
- return newTxEvents[0].args["txId"];
790
804
  };
791
805
 
792
806
  // src/config/transactions.ts
@@ -1672,6 +1686,7 @@ var stakingActions = (client, publicClient) => {
1672
1686
  const contract = getReadOnlyStakingContract();
1673
1687
  const [
1674
1688
  epoch,
1689
+ finalized,
1675
1690
  validatorMinStake,
1676
1691
  delegatorMinStake,
1677
1692
  activeCount,
@@ -1681,6 +1696,7 @@ var stakingActions = (client, publicClient) => {
1681
1696
  epochEven
1682
1697
  ] = await Promise.all([
1683
1698
  contract.read.epoch(),
1699
+ contract.read.finalized(),
1684
1700
  contract.read.validatorMinStake(),
1685
1701
  contract.read.delegatorMinStake(),
1686
1702
  contract.read.activeValidatorsCount(),
@@ -1690,8 +1706,7 @@ var stakingActions = (client, publicClient) => {
1690
1706
  contract.read.epochEven()
1691
1707
  ]);
1692
1708
  const currentEpochData = epoch % 2n === 0n ? epochEven : epochOdd;
1693
- const currentEpochStart = new Date(Number(currentEpochData.start) * 1e3);
1694
- const currentEpochEnd = currentEpochData.end > 0n ? new Date(Number(currentEpochData.end) * 1e3) : null;
1709
+ const currentEpochEnd = currentEpochData.end > 0n;
1695
1710
  let nextEpochEstimate = null;
1696
1711
  if (!currentEpochEnd) {
1697
1712
  const duration = epoch === 0n ? epochZeroMinDuration : epochMinDuration;
@@ -1700,20 +1715,42 @@ var stakingActions = (client, publicClient) => {
1700
1715
  }
1701
1716
  return {
1702
1717
  currentEpoch: epoch,
1718
+ lastFinalizedEpoch: finalized,
1703
1719
  validatorMinStake: formatStakingAmount(validatorMinStake),
1704
1720
  validatorMinStakeRaw: validatorMinStake,
1705
1721
  delegatorMinStake: formatStakingAmount(delegatorMinStake),
1706
1722
  delegatorMinStakeRaw: delegatorMinStake,
1707
1723
  activeValidatorsCount: activeCount,
1708
1724
  epochMinDuration,
1709
- currentEpochStart,
1710
- currentEpochEnd,
1711
- nextEpochEstimate,
1712
- inflation: formatStakingAmount(currentEpochData.inflation),
1713
- inflationRaw: currentEpochData.inflation,
1714
- totalWeight: currentEpochData.weight,
1715
- totalClaimed: formatStakingAmount(currentEpochData.claimed),
1716
- totalClaimedRaw: currentEpochData.claimed
1725
+ nextEpochEstimate
1726
+ };
1727
+ },
1728
+ getEpochData: async (epochNumber) => {
1729
+ const contract = getReadOnlyStakingContract();
1730
+ const [currentEpoch, epochOdd, epochEven] = await Promise.all([
1731
+ contract.read.epoch(),
1732
+ contract.read.epochOdd(),
1733
+ contract.read.epochEven()
1734
+ ]);
1735
+ if (epochNumber > currentEpoch) {
1736
+ throw new Error(`Epoch ${epochNumber} has not started yet (current: ${currentEpoch})`);
1737
+ }
1738
+ if (epochNumber < currentEpoch - 1n && currentEpoch > 0n) {
1739
+ throw new Error(`Epoch ${epochNumber} data no longer available (only current and previous epoch stored)`);
1740
+ }
1741
+ const epochData = epochNumber % 2n === 0n ? epochEven : epochOdd;
1742
+ return {
1743
+ start: epochData.start,
1744
+ end: epochData.end,
1745
+ inflation: epochData.inflation,
1746
+ weight: epochData.weight,
1747
+ weightDeposit: epochData.weightDeposit,
1748
+ weightWithdrawal: epochData.weightWithdrawal,
1749
+ vcount: epochData.vcount,
1750
+ claimed: epochData.claimed,
1751
+ stakeDeposit: epochData.stakeDeposit,
1752
+ stakeWithdrawal: epochData.stakeWithdrawal,
1753
+ slashed: epochData.slashed
1717
1754
  };
1718
1755
  },
1719
1756
  getActiveValidators: async () => {
@@ -1747,6 +1784,11 @@ var stakingActions = (client, publicClient) => {
1747
1784
  permanentlyBanned: v.permanentlyBanned
1748
1785
  }));
1749
1786
  },
1787
+ getSlashingAddress: async () => {
1788
+ const contract = getReadOnlyStakingContract();
1789
+ const externalContracts = await contract.read.contracts();
1790
+ return externalContracts[4];
1791
+ },
1750
1792
  getStakingContract,
1751
1793
  parseStakingAmount,
1752
1794
  formatStakingAmount
@@ -1785,7 +1827,7 @@ function chainActions(client) {
1785
1827
  }
1786
1828
 
1787
1829
  // src/client/client.ts
1788
- var getCustomTransportConfig = (config) => {
1830
+ var getCustomTransportConfig = (config, chainConfig) => {
1789
1831
  const isAddress = typeof config.account !== "object";
1790
1832
  return {
1791
1833
  async request({ method, params = [] }) {
@@ -1801,11 +1843,8 @@ var getCustomTransportConfig = (config) => {
1801
1843
  }
1802
1844
  }
1803
1845
  {
1804
- if (!config.chain) {
1805
- throw new Error("Chain is not set");
1806
- }
1807
1846
  try {
1808
- const response = await fetch(config.chain.rpcUrls.default.http[0], {
1847
+ const response = await fetch(chainConfig.rpcUrls.default.http[0], {
1809
1848
  method: "POST",
1810
1849
  headers: {
1811
1850
  "Content-Type": "application/json"
@@ -1835,7 +1874,7 @@ var createClient = (config = { chain: localnet }) => {
1835
1874
  if (config.endpoint) {
1836
1875
  chainConfig.rpcUrls.default.http = [config.endpoint];
1837
1876
  }
1838
- const customTransport = custom(getCustomTransportConfig(config), { retryCount: 0, retryDelay: 0 });
1877
+ const customTransport = custom(getCustomTransportConfig(config, chainConfig), { retryCount: 0, retryDelay: 0 });
1839
1878
  const publicClient = createPublicClient(chainConfig, customTransport).extend(
1840
1879
  publicActions
1841
1880
  );
@@ -1,3 +1,3 @@
1
1
  export { Account, Address } from 'viem';
2
- export { I as BannedValidatorInfo, d as CalldataAddress, C as CalldataEncodable, i as ContractMethod, h as ContractMethodBase, f as ContractParamsArraySchemaElement, g as ContractParamsSchema, j as ContractSchema, o as DECIDED_STATES, a as DecodedCallData, D as DecodedDeployData, a3 as DelegatorClaimOptions, a2 as DelegatorExitOptions, a1 as DelegatorJoinOptions, R as DelegatorJoinResult, K as EpochData, L as EpochInfo, G as GenLayerClient, e as GenLayerMethod, b as GenLayerRawTransaction, c as GenLayerTransaction, H as Hash, M as MethodDescription, N as Network, P as PendingDeposit, F as PendingWithdrawal, a0 as SetIdentityOptions, $ as SetOperatorOptions, y as SnapSource, J as StakeInfo, a4 as StakingActions, z as StakingContract, O as StakingTransactionResult, k as TransactionHash, x as TransactionHashVariant, m as TransactionResult, r as TransactionResultNameToNumber, l as TransactionStatus, w as TransactionType, Z as ValidatorClaimOptions, X as ValidatorDepositOptions, Y as ValidatorExitOptions, B as ValidatorIdentity, E as ValidatorInfo, U as ValidatorJoinOptions, Q as ValidatorJoinResult, _ as ValidatorPrimeOptions, A as ValidatorView, s as VoteType, W as WithdrawalCommit, p as isDecidedState, q as transactionResultNumberToName, n as transactionsStatusNameToNumber, t as transactionsStatusNumberToName, u as voteTypeNameToNumber, v as voteTypeNumberToName } from '../index-DM646L5c.cjs';
2
+ export { I as BannedValidatorInfo, d as CalldataAddress, C as CalldataEncodable, i as ContractMethod, h as ContractMethodBase, f as ContractParamsArraySchemaElement, g as ContractParamsSchema, j as ContractSchema, o as DECIDED_STATES, a as DecodedCallData, D as DecodedDeployData, a4 as DelegatorClaimOptions, a3 as DelegatorExitOptions, a2 as DelegatorJoinOptions, U as DelegatorJoinResult, K as EpochData, O as EpochInfo, G as GenLayerClient, e as GenLayerMethod, b as GenLayerRawTransaction, c as GenLayerTransaction, H as Hash, L as LeaderReceipt, M as MethodDescription, N as Network, P as PendingDeposit, F as PendingWithdrawal, a1 as SetIdentityOptions, a0 as SetOperatorOptions, y as SnapSource, J as StakeInfo, a5 as StakingActions, z as StakingContract, Q as StakingTransactionResult, k as TransactionHash, x as TransactionHashVariant, m as TransactionResult, r as TransactionResultNameToNumber, l as TransactionStatus, w as TransactionType, _ as ValidatorClaimOptions, Y as ValidatorDepositOptions, Z as ValidatorExitOptions, B as ValidatorIdentity, E as ValidatorInfo, X as ValidatorJoinOptions, R as ValidatorJoinResult, $ as ValidatorPrimeOptions, A as ValidatorView, s as VoteType, W as WithdrawalCommit, p as isDecidedState, q as transactionResultNumberToName, n as transactionsStatusNameToNumber, t as transactionsStatusNumberToName, u as voteTypeNameToNumber, v as voteTypeNumberToName } from '../index-D9ONjYgl.cjs';
3
3
  export { G as GenLayerChain } from '../chains-B7B7UXdn.cjs';
@@ -1,3 +1,3 @@
1
1
  export { Account, Address } from 'viem';
2
- export { I as BannedValidatorInfo, d as CalldataAddress, C as CalldataEncodable, i as ContractMethod, h as ContractMethodBase, f as ContractParamsArraySchemaElement, g as ContractParamsSchema, j as ContractSchema, o as DECIDED_STATES, a as DecodedCallData, D as DecodedDeployData, a3 as DelegatorClaimOptions, a2 as DelegatorExitOptions, a1 as DelegatorJoinOptions, R as DelegatorJoinResult, K as EpochData, L as EpochInfo, G as GenLayerClient, e as GenLayerMethod, b as GenLayerRawTransaction, c as GenLayerTransaction, H as Hash, M as MethodDescription, N as Network, P as PendingDeposit, F as PendingWithdrawal, a0 as SetIdentityOptions, $ as SetOperatorOptions, y as SnapSource, J as StakeInfo, a4 as StakingActions, z as StakingContract, O as StakingTransactionResult, k as TransactionHash, x as TransactionHashVariant, m as TransactionResult, r as TransactionResultNameToNumber, l as TransactionStatus, w as TransactionType, Z as ValidatorClaimOptions, X as ValidatorDepositOptions, Y as ValidatorExitOptions, B as ValidatorIdentity, E as ValidatorInfo, U as ValidatorJoinOptions, Q as ValidatorJoinResult, _ as ValidatorPrimeOptions, A as ValidatorView, s as VoteType, W as WithdrawalCommit, p as isDecidedState, q as transactionResultNumberToName, n as transactionsStatusNameToNumber, t as transactionsStatusNumberToName, u as voteTypeNameToNumber, v as voteTypeNumberToName } from '../index-9QkkDjYm.js';
2
+ export { I as BannedValidatorInfo, d as CalldataAddress, C as CalldataEncodable, i as ContractMethod, h as ContractMethodBase, f as ContractParamsArraySchemaElement, g as ContractParamsSchema, j as ContractSchema, o as DECIDED_STATES, a as DecodedCallData, D as DecodedDeployData, a4 as DelegatorClaimOptions, a3 as DelegatorExitOptions, a2 as DelegatorJoinOptions, U as DelegatorJoinResult, K as EpochData, O as EpochInfo, G as GenLayerClient, e as GenLayerMethod, b as GenLayerRawTransaction, c as GenLayerTransaction, H as Hash, L as LeaderReceipt, M as MethodDescription, N as Network, P as PendingDeposit, F as PendingWithdrawal, a1 as SetIdentityOptions, a0 as SetOperatorOptions, y as SnapSource, J as StakeInfo, a5 as StakingActions, z as StakingContract, Q as StakingTransactionResult, k as TransactionHash, x as TransactionHashVariant, m as TransactionResult, r as TransactionResultNameToNumber, l as TransactionStatus, w as TransactionType, _ as ValidatorClaimOptions, Y as ValidatorDepositOptions, Z as ValidatorExitOptions, B as ValidatorIdentity, E as ValidatorInfo, X as ValidatorJoinOptions, R as ValidatorJoinResult, $ as ValidatorPrimeOptions, A as ValidatorView, s as VoteType, W as WithdrawalCommit, p as isDecidedState, q as transactionResultNumberToName, n as transactionsStatusNameToNumber, t as transactionsStatusNumberToName, u as voteTypeNameToNumber, v as voteTypeNumberToName } from '../index-ZDqJWXj0.js';
3
3
  export { G as GenLayerChain } from '../chains-B7B7UXdn.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "genlayer-js",
3
3
  "type": "module",
4
- "version": "0.18.7",
4
+ "version": "0.18.9",
5
5
  "description": "GenLayer JavaScript SDK",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -6,6 +6,9 @@ export const VALIDATOR_WALLET_ABI = [
6
6
  {name: "TransferFailed", type: "error", inputs: []},
7
7
  {name: "OperatorTransferNotReady", type: "error", inputs: []},
8
8
  {name: "NoPendingOperator", type: "error", inputs: []},
9
+ // OpenZeppelin Ownable errors
10
+ {name: "OwnableUnauthorizedAccount", type: "error", inputs: [{name: "account", type: "address"}]},
11
+ {name: "OwnableInvalidOwner", type: "error", inputs: [{name: "owner", type: "address"}]},
9
12
 
10
13
  // Functions
11
14
  {
@@ -69,6 +72,67 @@ export const VALIDATOR_WALLET_ABI = [
69
72
  ],
70
73
  outputs: [],
71
74
  },
75
+ // Staking functions (forwarded to staking contract)
76
+ {
77
+ name: "validatorDeposit",
78
+ type: "function",
79
+ stateMutability: "payable",
80
+ inputs: [],
81
+ outputs: [],
82
+ },
83
+ {
84
+ name: "validatorExit",
85
+ type: "function",
86
+ stateMutability: "nonpayable",
87
+ inputs: [{name: "_shares", type: "uint256"}],
88
+ outputs: [],
89
+ },
90
+ {
91
+ name: "validatorClaim",
92
+ type: "function",
93
+ stateMutability: "nonpayable",
94
+ inputs: [],
95
+ outputs: [],
96
+ },
97
+ // Two-step operator transfer
98
+ {
99
+ name: "initiateOperatorTransfer",
100
+ type: "function",
101
+ stateMutability: "nonpayable",
102
+ inputs: [{name: "_newOperator", type: "address"}],
103
+ outputs: [],
104
+ },
105
+ {
106
+ name: "completeOperatorTransfer",
107
+ type: "function",
108
+ stateMutability: "nonpayable",
109
+ inputs: [],
110
+ outputs: [],
111
+ },
112
+ {
113
+ name: "cancelOperatorTransfer",
114
+ type: "function",
115
+ stateMutability: "nonpayable",
116
+ inputs: [],
117
+ outputs: [],
118
+ },
119
+ {
120
+ name: "getPendingOperator",
121
+ type: "function",
122
+ stateMutability: "view",
123
+ inputs: [],
124
+ outputs: [
125
+ {name: "", type: "address"},
126
+ {name: "", type: "uint256"},
127
+ ],
128
+ },
129
+ {
130
+ name: "getOperator",
131
+ type: "function",
132
+ stateMutability: "view",
133
+ inputs: [],
134
+ outputs: [{name: "", type: "address"}],
135
+ },
72
136
  ] as const;
73
137
 
74
138
  export const STAKING_ABI = [
@@ -250,6 +314,13 @@ export const STAKING_ABI = [
250
314
  inputs: [],
251
315
  outputs: [{name: "", type: "uint256"}],
252
316
  },
317
+ {
318
+ name: "finalized",
319
+ type: "function",
320
+ stateMutability: "view",
321
+ inputs: [],
322
+ outputs: [{name: "", type: "uint256"}],
323
+ },
253
324
  {
254
325
  name: "validatorMinStake",
255
326
  type: "function",
@@ -298,6 +369,7 @@ export const STAKING_ABI = [
298
369
  {name: "claimed", type: "uint256"},
299
370
  {name: "stakeDeposit", type: "uint256"},
300
371
  {name: "stakeWithdrawal", type: "uint256"},
372
+ {name: "slashed", type: "uint256"},
301
373
  ],
302
374
  },
303
375
  ],
@@ -322,6 +394,7 @@ export const STAKING_ABI = [
322
394
  {name: "claimed", type: "uint256"},
323
395
  {name: "stakeDeposit", type: "uint256"},
324
396
  {name: "stakeWithdrawal", type: "uint256"},
397
+ {name: "slashed", type: "uint256"},
325
398
  ],
326
399
  },
327
400
  ],
@@ -562,4 +635,53 @@ export const STAKING_ABI = [
562
635
  {name: "amount", type: "uint256", indexed: false},
563
636
  ],
564
637
  },
638
+ {
639
+ name: "ValidatorPrime",
640
+ type: "event",
641
+ inputs: [
642
+ {name: "validator", type: "address", indexed: false},
643
+ {name: "epoch", type: "uint256", indexed: false},
644
+ {name: "validatorRewards", type: "uint256", indexed: false},
645
+ {name: "delegatorRewards", type: "uint256", indexed: false},
646
+ ],
647
+ },
648
+ // External contracts getter
649
+ {
650
+ name: "contracts",
651
+ type: "function",
652
+ stateMutability: "view",
653
+ inputs: [],
654
+ outputs: [
655
+ {name: "gen", type: "address"},
656
+ {name: "transactions", type: "address"},
657
+ {name: "idleness", type: "address"},
658
+ {name: "tribunal", type: "address"},
659
+ {name: "slashing", type: "address"},
660
+ {name: "consensus", type: "address"},
661
+ {name: "validatorWalletFactory", type: "address"},
662
+ {name: "nftMinter", type: "address"},
663
+ ],
664
+ },
665
+ ] as const;
666
+
667
+ // Slash contract ABI for slashing events
668
+ export const SLASH_ABI = [
669
+ {
670
+ name: "SlashedFromIdleness",
671
+ type: "event",
672
+ inputs: [
673
+ {name: "validator", type: "address", indexed: true},
674
+ {name: "txId", type: "bytes32", indexed: false},
675
+ {name: "epoch", type: "uint256", indexed: false},
676
+ {name: "percentage", type: "uint256", indexed: false},
677
+ ],
678
+ },
679
+ {
680
+ name: "SlashEnacted",
681
+ type: "event",
682
+ inputs: [
683
+ {name: "validator", type: "address", indexed: true},
684
+ {name: "epoch", type: "uint256", indexed: false},
685
+ ],
686
+ },
565
687
  ] as const;
@@ -33,7 +33,7 @@ interface ClientConfig {
33
33
  provider?: EthereumProvider; // Custom provider for wallet framework integration
34
34
  }
35
35
 
36
- const getCustomTransportConfig = (config: ClientConfig) => {
36
+ const getCustomTransportConfig = (config: ClientConfig, chainConfig: GenLayerChain) => {
37
37
  const isAddress = typeof config.account !== "object";
38
38
 
39
39
  return {
@@ -51,12 +51,8 @@ const getCustomTransportConfig = (config: ClientConfig) => {
51
51
  }
52
52
 
53
53
  {
54
- if (!config.chain) {
55
- throw new Error("Chain is not set");
56
- }
57
-
58
54
  try {
59
- const response = await fetch(config.chain.rpcUrls.default.http[0], {
55
+ const response = await fetch(chainConfig.rpcUrls.default.http[0], {
60
56
  method: "POST",
61
57
  headers: {
62
58
  "Content-Type": "application/json",
@@ -91,7 +87,7 @@ export const createClient = (config: ClientConfig = {chain: localnet}): GenLayer
91
87
  chainConfig.rpcUrls.default.http = [config.endpoint];
92
88
  }
93
89
 
94
- const customTransport = custom(getCustomTransportConfig(config), {retryCount: 0, retryDelay: 0});
90
+ const customTransport = custom(getCustomTransportConfig(config, chainConfig as GenLayerChain), {retryCount: 0, retryDelay: 0});
95
91
  const publicClient = createPublicClient(chainConfig as GenLayerChain, customTransport).extend(
96
92
  publicActions,
97
93
  );
@@ -301,8 +301,8 @@ const _sendTransaction = async ({
301
301
  }
302
302
 
303
303
  const validatedSenderAccount = validateAccount(senderAccount);
304
-
305
304
  const nonce = await client.getCurrentNonce({address: validatedSenderAccount.address});
305
+
306
306
  let estimatedGas: bigint;
307
307
  try {
308
308
  estimatedGas = await client.estimateTransactionGas({
@@ -311,58 +311,77 @@ const _sendTransaction = async ({
311
311
  data: encodedData,
312
312
  value: value,
313
313
  });
314
- } catch (error) {
315
- console.warn("Gas estimation failed, using fallback value:", error);
314
+ } catch (err) {
315
+ console.error("Gas estimation failed, using default 200_000:", err);
316
316
  estimatedGas = 200_000n;
317
317
  }
318
- const transactionRequest = await client.prepareTransactionRequest({
319
- account: validatedSenderAccount,
320
- to: client.chain.consensusMainContract?.address as Address,
321
- data: encodedData,
322
- type: "legacy",
323
- nonce: Number(nonce),
324
- value: value,
325
- gas: estimatedGas,
326
- });
327
318
 
328
- if (validatedSenderAccount?.type !== "local") {
329
- const formattedRequest = {
330
- from: transactionRequest.from,
331
- to: transactionRequest.to,
319
+ // For local accounts, build transaction request directly to avoid viem's
320
+ // prepareTransactionRequest which calls eth_fillTransaction (unsupported by GenLayer RPC)
321
+ if (validatedSenderAccount?.type === "local") {
322
+ if (!validatedSenderAccount?.signTransaction) {
323
+ throw new Error("Account does not support signTransaction");
324
+ }
325
+
326
+ const gasPriceHex = (await client.request({
327
+ method: "eth_gasPrice",
328
+ })) as string;
329
+
330
+ const transactionRequest = {
331
+ account: validatedSenderAccount,
332
+ to: client.chain.consensusMainContract?.address as Address,
332
333
  data: encodedData,
333
- value: transactionRequest.value ? `0x${transactionRequest.value.toString(16)}` : "0x0",
334
- gas: transactionRequest.gas ? `0x${transactionRequest.gas.toString(16)}` : "0x5208",
334
+ type: "legacy" as const,
335
+ nonce: Number(nonce),
336
+ value: value,
337
+ gas: estimatedGas,
338
+ gasPrice: BigInt(gasPriceHex),
339
+ chainId: client.chain.id,
335
340
  };
336
341
 
337
- return await client.request({
338
- method: "eth_sendTransaction",
339
- params: [formattedRequest as any],
340
- });
341
- }
342
+ const serializedTransaction = await validatedSenderAccount.signTransaction(transactionRequest);
343
+ const txHash = await client.sendRawTransaction({serializedTransaction: serializedTransaction});
344
+ const receipt = await publicClient.waitForTransactionReceipt({hash: txHash});
342
345
 
343
- if (!validatedSenderAccount?.signTransaction) {
344
- throw new Error("Account does not support signTransaction");
345
- }
346
-
347
- const serializedTransaction = await validatedSenderAccount.signTransaction(transactionRequest);
346
+ if (receipt.status === "reverted") {
347
+ throw new Error("Transaction reverted");
348
+ }
348
349
 
349
- const txHash = await client.sendRawTransaction({serializedTransaction: serializedTransaction});
350
+ const newTxEvents = parseEventLogs({
351
+ abi: client.chain.consensusMainContract?.abi as any,
352
+ eventName: "NewTransaction",
353
+ logs: receipt.logs,
354
+ }) as unknown as {args: {txId: `0x${string}`}}[];
350
355
 
351
- const receipt = await publicClient.waitForTransactionReceipt({hash: txHash});
356
+ if (newTxEvents.length === 0) {
357
+ throw new Error("Transaction not processed by consensus");
358
+ }
352
359
 
353
- if (receipt.status === "reverted") {
354
- throw new Error("Transaction reverted");
360
+ return newTxEvents[0].args["txId"];
355
361
  }
356
362
 
357
- const newTxEvents = parseEventLogs({
358
- abi: client.chain.consensusMainContract?.abi as any,
359
- eventName: "NewTransaction",
360
- logs: receipt.logs,
361
- }) as unknown as {args: {txId: `0x${string}`}}[];
363
+ // For external wallets (e.g., MetaMask via AppKit), use prepareTransactionRequest
364
+ // which will route eth_* calls through the provider
365
+ const transactionRequest = await client.prepareTransactionRequest({
366
+ account: validatedSenderAccount,
367
+ to: client.chain.consensusMainContract?.address as Address,
368
+ data: encodedData,
369
+ type: "legacy",
370
+ nonce: Number(nonce),
371
+ value: value,
372
+ gas: estimatedGas,
373
+ });
362
374
 
363
- if (newTxEvents.length === 0) {
364
- throw new Error("Transaction not processed by consensus");
365
- }
375
+ const formattedRequest = {
376
+ from: transactionRequest.from,
377
+ to: transactionRequest.to,
378
+ data: encodedData,
379
+ value: transactionRequest.value ? `0x${transactionRequest.value.toString(16)}` : "0x0",
380
+ gas: transactionRequest.gas ? `0x${transactionRequest.gas.toString(16)}` : "0x5208",
381
+ };
366
382
 
367
- return newTxEvents[0].args["txId"];
383
+ return await client.request({
384
+ method: "eth_sendTransaction",
385
+ params: [formattedRequest as any],
386
+ });
368
387
  };