genlayer-js 0.18.8 → 0.18.10
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/CHANGELOG.md +4 -0
- package/dist/chains/index.cjs +2 -2
- package/dist/chains/index.js +1 -1
- package/dist/{chunk-WTAFQOXC.cjs → chunk-PPBY3UXF.cjs} +48 -7
- package/dist/{chunk-V4ZFI4GV.js → chunk-WZNF2WK4.js} +48 -7
- package/dist/{index-C3KT8eu_.d.cts → index-D9ONjYgl.d.cts} +99 -22
- package/dist/{index-BNui_XYa.d.ts → index-ZDqJWXj0.d.ts} +99 -22
- package/dist/index.cjs +133 -94
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +88 -49
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/abi/staking.ts +61 -0
- package/src/chains/testnetAsimov.ts +6 -4
- package/src/client/client.ts +3 -7
- package/src/contracts/actions.ts +60 -41
- package/src/staking/actions.ts +49 -11
- package/src/types/staking.ts +4 -7
- package/src/types/transactions.ts +16 -14
- package/tests/smoke.test.ts +59 -0
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
localnet,
|
|
6
6
|
studionet,
|
|
7
7
|
testnetAsimov
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-WZNF2WK4.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 (
|
|
747
|
-
console.
|
|
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
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
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
|
|
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
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
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(
|
|
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
|
);
|
package/dist/types/index.d.cts
CHANGED
|
@@ -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,
|
|
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';
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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,
|
|
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
package/src/abi/staking.ts
CHANGED
|
@@ -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
|
{
|
|
@@ -311,6 +314,13 @@ export const STAKING_ABI = [
|
|
|
311
314
|
inputs: [],
|
|
312
315
|
outputs: [{name: "", type: "uint256"}],
|
|
313
316
|
},
|
|
317
|
+
{
|
|
318
|
+
name: "finalized",
|
|
319
|
+
type: "function",
|
|
320
|
+
stateMutability: "view",
|
|
321
|
+
inputs: [],
|
|
322
|
+
outputs: [{name: "", type: "uint256"}],
|
|
323
|
+
},
|
|
314
324
|
{
|
|
315
325
|
name: "validatorMinStake",
|
|
316
326
|
type: "function",
|
|
@@ -359,6 +369,7 @@ export const STAKING_ABI = [
|
|
|
359
369
|
{name: "claimed", type: "uint256"},
|
|
360
370
|
{name: "stakeDeposit", type: "uint256"},
|
|
361
371
|
{name: "stakeWithdrawal", type: "uint256"},
|
|
372
|
+
{name: "slashed", type: "uint256"},
|
|
362
373
|
],
|
|
363
374
|
},
|
|
364
375
|
],
|
|
@@ -383,6 +394,7 @@ export const STAKING_ABI = [
|
|
|
383
394
|
{name: "claimed", type: "uint256"},
|
|
384
395
|
{name: "stakeDeposit", type: "uint256"},
|
|
385
396
|
{name: "stakeWithdrawal", type: "uint256"},
|
|
397
|
+
{name: "slashed", type: "uint256"},
|
|
386
398
|
],
|
|
387
399
|
},
|
|
388
400
|
],
|
|
@@ -623,4 +635,53 @@ export const STAKING_ABI = [
|
|
|
623
635
|
{name: "amount", type: "uint256", indexed: false},
|
|
624
636
|
],
|
|
625
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
|
+
},
|
|
626
687
|
] as const;
|
|
@@ -3,15 +3,16 @@ import {GenLayerChain} from "@/types";
|
|
|
3
3
|
import {STAKING_ABI} from "@/abi/staking";
|
|
4
4
|
|
|
5
5
|
// chains/localnet.ts
|
|
6
|
-
const TESTNET_JSON_RPC_URL = "https://
|
|
6
|
+
const TESTNET_JSON_RPC_URL = "https://zksync-os-testnet-genlayer.zksync.dev";
|
|
7
|
+
const TESTNET_WS_URL = "wss://zksync-os-testnet-alpha.zksync.dev/ws";
|
|
7
8
|
|
|
8
9
|
const STAKING_CONTRACT = {
|
|
9
|
-
address: "
|
|
10
|
+
address: "0x63Fa5E0bb10fb6fA98F44726C5518223F767687A" as Address,
|
|
10
11
|
abi: STAKING_ABI,
|
|
11
12
|
};
|
|
12
13
|
const EXPLORER_URL = "https://explorer-asimov.genlayer.com/";
|
|
13
14
|
const CONSENSUS_MAIN_CONTRACT = {
|
|
14
|
-
address: "
|
|
15
|
+
address: "0x6CAFF6769d70824745AD895663409DC70aB5B28E" as Address,
|
|
15
16
|
abi: [
|
|
16
17
|
{
|
|
17
18
|
inputs: [],
|
|
@@ -1401,7 +1402,7 @@ const CONSENSUS_MAIN_CONTRACT = {
|
|
|
1401
1402
|
};
|
|
1402
1403
|
|
|
1403
1404
|
const CONSENSUS_DATA_CONTRACT = {
|
|
1404
|
-
address: "
|
|
1405
|
+
address: "0x0D9d1d74d72Fa5eB94bcf746C8FCcb312a722c9B" as Address,
|
|
1405
1406
|
abi: [
|
|
1406
1407
|
{
|
|
1407
1408
|
inputs: [],
|
|
@@ -3989,6 +3990,7 @@ export const testnetAsimov: GenLayerChain = defineChain({
|
|
|
3989
3990
|
rpcUrls: {
|
|
3990
3991
|
default: {
|
|
3991
3992
|
http: [TESTNET_JSON_RPC_URL],
|
|
3993
|
+
webSocket: [TESTNET_WS_URL],
|
|
3992
3994
|
},
|
|
3993
3995
|
},
|
|
3994
3996
|
nativeCurrency: {
|
package/src/client/client.ts
CHANGED
|
@@ -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(
|
|
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
|
);
|
package/src/contracts/actions.ts
CHANGED
|
@@ -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 (
|
|
315
|
-
console.
|
|
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
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
334
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
const serializedTransaction = await validatedSenderAccount.signTransaction(transactionRequest);
|
|
346
|
+
if (receipt.status === "reverted") {
|
|
347
|
+
throw new Error("Transaction reverted");
|
|
348
|
+
}
|
|
348
349
|
|
|
349
|
-
|
|
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
|
-
|
|
356
|
+
if (newTxEvents.length === 0) {
|
|
357
|
+
throw new Error("Transaction not processed by consensus");
|
|
358
|
+
}
|
|
352
359
|
|
|
353
|
-
|
|
354
|
-
throw new Error("Transaction reverted");
|
|
360
|
+
return newTxEvents[0].args["txId"];
|
|
355
361
|
}
|
|
356
362
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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
|
-
|
|
364
|
-
|
|
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
|
|
383
|
+
return await client.request({
|
|
384
|
+
method: "eth_sendTransaction",
|
|
385
|
+
params: [formattedRequest as any],
|
|
386
|
+
});
|
|
368
387
|
};
|
package/src/staking/actions.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
BannedValidatorInfo,
|
|
9
9
|
StakeInfo,
|
|
10
10
|
EpochInfo,
|
|
11
|
+
EpochData,
|
|
11
12
|
StakingTransactionResult,
|
|
12
13
|
ValidatorJoinResult,
|
|
13
14
|
DelegatorJoinResult,
|
|
@@ -559,6 +560,7 @@ export const stakingActions = (
|
|
|
559
560
|
|
|
560
561
|
const [
|
|
561
562
|
epoch,
|
|
563
|
+
finalized,
|
|
562
564
|
validatorMinStake,
|
|
563
565
|
delegatorMinStake,
|
|
564
566
|
activeCount,
|
|
@@ -568,6 +570,7 @@ export const stakingActions = (
|
|
|
568
570
|
epochEven,
|
|
569
571
|
] = await Promise.all([
|
|
570
572
|
contract.read.epoch() as Promise<bigint>,
|
|
573
|
+
contract.read.finalized() as Promise<bigint>,
|
|
571
574
|
contract.read.validatorMinStake() as Promise<bigint>,
|
|
572
575
|
contract.read.delegatorMinStake() as Promise<bigint>,
|
|
573
576
|
contract.read.activeValidatorsCount() as Promise<bigint>,
|
|
@@ -577,13 +580,11 @@ export const stakingActions = (
|
|
|
577
580
|
contract.read.epochEven() as Promise<any>,
|
|
578
581
|
]);
|
|
579
582
|
|
|
580
|
-
// Current epoch data
|
|
583
|
+
// Current epoch data for next epoch estimate
|
|
581
584
|
const currentEpochData = epoch % 2n === 0n ? epochEven : epochOdd;
|
|
582
|
-
const
|
|
583
|
-
const currentEpochEnd = currentEpochData.end > 0n ? new Date(Number(currentEpochData.end) * 1000) : null;
|
|
585
|
+
const currentEpochEnd = currentEpochData.end > 0n;
|
|
584
586
|
|
|
585
587
|
// Estimate next epoch: current start + min duration (if epoch hasn't ended)
|
|
586
|
-
// Epoch 0 uses epochZeroMinDuration, all other epochs use epochMinDuration
|
|
587
588
|
let nextEpochEstimate: Date | null = null;
|
|
588
589
|
if (!currentEpochEnd) {
|
|
589
590
|
const duration = epoch === 0n ? epochZeroMinDuration : epochMinDuration;
|
|
@@ -593,20 +594,50 @@ export const stakingActions = (
|
|
|
593
594
|
|
|
594
595
|
return {
|
|
595
596
|
currentEpoch: epoch,
|
|
597
|
+
lastFinalizedEpoch: finalized,
|
|
596
598
|
validatorMinStake: formatStakingAmount(validatorMinStake),
|
|
597
599
|
validatorMinStakeRaw: validatorMinStake,
|
|
598
600
|
delegatorMinStake: formatStakingAmount(delegatorMinStake),
|
|
599
601
|
delegatorMinStakeRaw: delegatorMinStake,
|
|
600
602
|
activeValidatorsCount: activeCount,
|
|
601
603
|
epochMinDuration,
|
|
602
|
-
currentEpochStart,
|
|
603
|
-
currentEpochEnd,
|
|
604
604
|
nextEpochEstimate,
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
605
|
+
};
|
|
606
|
+
},
|
|
607
|
+
|
|
608
|
+
getEpochData: async (epochNumber: bigint): Promise<EpochData> => {
|
|
609
|
+
const contract = getReadOnlyStakingContract();
|
|
610
|
+
|
|
611
|
+
const [currentEpoch, epochOdd, epochEven] = await Promise.all([
|
|
612
|
+
contract.read.epoch() as Promise<bigint>,
|
|
613
|
+
contract.read.epochOdd() as Promise<any>,
|
|
614
|
+
contract.read.epochEven() as Promise<any>,
|
|
615
|
+
]);
|
|
616
|
+
|
|
617
|
+
// Epochs alternate between odd/even storage slots
|
|
618
|
+
// Current epoch N uses: N % 2 === 0 ? epochEven : epochOdd
|
|
619
|
+
// We can only access current epoch and previous epoch (N-1)
|
|
620
|
+
if (epochNumber > currentEpoch) {
|
|
621
|
+
throw new Error(`Epoch ${epochNumber} has not started yet (current: ${currentEpoch})`);
|
|
622
|
+
}
|
|
623
|
+
if (epochNumber < currentEpoch - 1n && currentEpoch > 0n) {
|
|
624
|
+
throw new Error(`Epoch ${epochNumber} data no longer available (only current and previous epoch stored)`);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
const epochData = epochNumber % 2n === 0n ? epochEven : epochOdd;
|
|
628
|
+
|
|
629
|
+
return {
|
|
630
|
+
start: epochData.start,
|
|
631
|
+
end: epochData.end,
|
|
632
|
+
inflation: epochData.inflation,
|
|
633
|
+
weight: epochData.weight,
|
|
634
|
+
weightDeposit: epochData.weightDeposit,
|
|
635
|
+
weightWithdrawal: epochData.weightWithdrawal,
|
|
636
|
+
vcount: epochData.vcount,
|
|
637
|
+
claimed: epochData.claimed,
|
|
638
|
+
stakeDeposit: epochData.stakeDeposit,
|
|
639
|
+
stakeWithdrawal: epochData.stakeWithdrawal,
|
|
640
|
+
slashed: epochData.slashed,
|
|
610
641
|
};
|
|
611
642
|
},
|
|
612
643
|
|
|
@@ -646,6 +677,13 @@ export const stakingActions = (
|
|
|
646
677
|
}));
|
|
647
678
|
},
|
|
648
679
|
|
|
680
|
+
getSlashingAddress: async (): Promise<Address> => {
|
|
681
|
+
const contract = getReadOnlyStakingContract();
|
|
682
|
+
// contracts() returns tuple: [gen, transactions, idleness, tribunal, slashing, consensus, validatorWalletFactory, nftMinter]
|
|
683
|
+
const externalContracts = (await contract.read.contracts()) as readonly ViemAddress[];
|
|
684
|
+
return externalContracts[4] as Address; // slashing is at index 4
|
|
685
|
+
},
|
|
686
|
+
|
|
649
687
|
getStakingContract,
|
|
650
688
|
parseStakingAmount,
|
|
651
689
|
formatStakingAmount,
|