@chorus-one/polygon 1.0.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.
Files changed (43) hide show
  1. package/.mocharc.json +6 -0
  2. package/LICENSE +13 -0
  3. package/README.md +233 -0
  4. package/dist/cjs/constants.d.ts +187 -0
  5. package/dist/cjs/constants.js +141 -0
  6. package/dist/cjs/index.d.ts +4 -0
  7. package/dist/cjs/index.js +12 -0
  8. package/dist/cjs/package.json +3 -0
  9. package/dist/cjs/referrer.d.ts +2 -0
  10. package/dist/cjs/referrer.js +15 -0
  11. package/dist/cjs/staker.d.ts +335 -0
  12. package/dist/cjs/staker.js +716 -0
  13. package/dist/cjs/types.d.ts +40 -0
  14. package/dist/cjs/types.js +2 -0
  15. package/dist/mjs/constants.d.ts +187 -0
  16. package/dist/mjs/constants.js +138 -0
  17. package/dist/mjs/index.d.ts +4 -0
  18. package/dist/mjs/index.js +2 -0
  19. package/dist/mjs/package.json +3 -0
  20. package/dist/mjs/referrer.d.ts +2 -0
  21. package/dist/mjs/referrer.js +11 -0
  22. package/dist/mjs/staker.d.ts +335 -0
  23. package/dist/mjs/staker.js +712 -0
  24. package/dist/mjs/types.d.ts +40 -0
  25. package/dist/mjs/types.js +1 -0
  26. package/hardhat.config.ts +27 -0
  27. package/package.json +50 -0
  28. package/src/constants.ts +151 -0
  29. package/src/index.ts +14 -0
  30. package/src/referrer.ts +15 -0
  31. package/src/staker.ts +878 -0
  32. package/src/types.ts +45 -0
  33. package/test/fixtures/expected-data.ts +17 -0
  34. package/test/integration/localSigner.spec.ts +128 -0
  35. package/test/integration/setup.ts +41 -0
  36. package/test/integration/staker.spec.ts +587 -0
  37. package/test/integration/testStaker.ts +130 -0
  38. package/test/integration/utils.ts +263 -0
  39. package/test/lib/networks.json +14 -0
  40. package/test/staker.spec.ts +154 -0
  41. package/tsconfig.cjs.json +9 -0
  42. package/tsconfig.json +13 -0
  43. package/tsconfig.mjs.json +9 -0
package/src/types.ts ADDED
@@ -0,0 +1,45 @@
1
+ import type { Hex, TransactionReceipt } from 'viem'
2
+ import type { PolygonNetworks } from './constants'
3
+
4
+ export interface PolygonNetworkConfig {
5
+ /** Network to use: 'mainnet' (Ethereum L1) or 'testnet' (Sepolia L1) */
6
+ network: PolygonNetworks
7
+ /** Optional RPC endpoint URL override. If not provided, uses viem's default for the network. */
8
+ rpcUrl?: string
9
+ }
10
+
11
+ export interface Transaction {
12
+ /** The recipient (contract) address in hexadecimal format */
13
+ to: Hex
14
+ /** The data to be included in the transaction in hexadecimal format (includes referrer tracking suffix) */
15
+ data: Hex
16
+ /** The amount of ETH (in wei) to be sent with the transaction */
17
+ value?: bigint
18
+ }
19
+
20
+ export interface PolygonTxStatus {
21
+ /** Status of the transaction */
22
+ status: 'success' | 'failure' | 'unknown'
23
+ /** Transaction receipt (null if unknown) */
24
+ receipt: TransactionReceipt | null
25
+ }
26
+
27
+ export interface StakeInfo {
28
+ /** Total staked amount formatted in POL */
29
+ balance: string
30
+ /** Total shares held by the delegator */
31
+ shares: bigint
32
+ /** Current exchange rate between shares and POL (with high precision) */
33
+ exchangeRate: bigint
34
+ }
35
+
36
+ export interface UnbondInfo {
37
+ /** Amount pending unbonding in POL */
38
+ amount: string
39
+ /** Whether the unbond can be withdrawn now */
40
+ isWithdrawable: boolean
41
+ /** Shares amount pending unbonding */
42
+ shares: bigint
43
+ /** Epoch number when the unbond becomes claimable */
44
+ withdrawEpoch: bigint
45
+ }
@@ -0,0 +1,17 @@
1
+ import type { Address, Hex } from 'viem'
2
+ import { NETWORK_CONTRACTS, CHORUS_ONE_POLYGON_VALIDATORS } from '../../src/constants'
3
+
4
+ const { stakingTokenAddress } = NETWORK_CONTRACTS.mainnet
5
+
6
+ export const TEST_ADDRESS: Address = '0x1234567890123456789012345678901234567890'
7
+ export const TEST_VALIDATOR_SHARE: Address = CHORUS_ONE_POLYGON_VALIDATORS.mainnet
8
+
9
+ export const EXPECTED_APPROVE_TX = {
10
+ amount: '100',
11
+ expected: {
12
+ to: stakingTokenAddress as Address,
13
+ // approve(address spender, uint256 amount) with spender = stakeManagerAddress (0x5e3e...), amount = 100e18
14
+ data: '0x095ea7b30000000000000000000000005e3ef299fddf15eaa0432e6e66473ace8c13d9080000000000000000000000000000000000000000000000056bc75e2d63100000' as Hex,
15
+ value: 0n
16
+ }
17
+ }
@@ -0,0 +1,128 @@
1
+ import { type PublicClient, parseEther } from 'viem'
2
+ import { hardhat } from 'viem/chains'
3
+ import { assert } from 'chai'
4
+ import { fundWithStakingToken, impersonate, getStakingTokenBalance, getWithdrawalDelay, advanceEpoch } from './utils'
5
+ import { restoreToInitialState } from './setup'
6
+ import { PolygonTestStaker } from './testStaker'
7
+
8
+ const TEST_MNEMONIC = 'test test test test test test test test test test test junk'
9
+ const AMOUNT = '100'
10
+
11
+ describe('PolygonStaker with LocalSigner', () => {
12
+ let testStaker: PolygonTestStaker
13
+ let publicClient: PublicClient
14
+
15
+ afterEach(async () => {
16
+ await restoreToInitialState()
17
+ })
18
+
19
+ beforeEach(async () => {
20
+ testStaker = new PolygonTestStaker({
21
+ mnemonic: TEST_MNEMONIC,
22
+ rpcUrl: hardhat.rpcUrls.default.http[0]
23
+ })
24
+ await testStaker.init()
25
+
26
+ publicClient = testStaker.publicClient
27
+
28
+ await fundWithStakingToken({
29
+ publicClient,
30
+ recipientAddress: testStaker.delegatorAddress,
31
+ amount: parseEther('10000')
32
+ })
33
+
34
+ await impersonate({ publicClient, address: testStaker.delegatorAddress })
35
+ })
36
+
37
+ it('approves and stakes using LocalSigner', async () => {
38
+ await testStaker.approve(AMOUNT)
39
+
40
+ const allowance = await testStaker.staker.getAllowance(testStaker.delegatorAddress)
41
+ assert.equal(allowance, AMOUNT)
42
+
43
+ await testStaker.stake(AMOUNT, 0n)
44
+
45
+ const stakeInfo = await testStaker.staker.getStake({
46
+ delegatorAddress: testStaker.delegatorAddress,
47
+ validatorShareAddress: testStaker.validatorShareAddress
48
+ })
49
+ assert.equal(stakeInfo.balance, AMOUNT)
50
+ })
51
+
52
+ it('unstakes using LocalSigner', async () => {
53
+ await testStaker.approve(AMOUNT)
54
+ await testStaker.stake(AMOUNT, 0n)
55
+
56
+ const nonceBefore = await testStaker.staker.getUnbondNonce({
57
+ delegatorAddress: testStaker.delegatorAddress,
58
+ validatorShareAddress: testStaker.validatorShareAddress
59
+ })
60
+
61
+ const stakeBefore = await testStaker.staker.getStake({
62
+ delegatorAddress: testStaker.delegatorAddress,
63
+ validatorShareAddress: testStaker.validatorShareAddress
64
+ })
65
+ await testStaker.unstake(AMOUNT, stakeBefore.shares)
66
+
67
+ const nonceAfter = await testStaker.staker.getUnbondNonce({
68
+ delegatorAddress: testStaker.delegatorAddress,
69
+ validatorShareAddress: testStaker.validatorShareAddress
70
+ })
71
+ assert.equal(nonceAfter, nonceBefore + 1n)
72
+
73
+ const stakeAfter = await testStaker.staker.getStake({
74
+ delegatorAddress: testStaker.delegatorAddress,
75
+ validatorShareAddress: testStaker.validatorShareAddress
76
+ })
77
+ assert.equal(stakeAfter.balance, '0')
78
+ })
79
+
80
+ it('withdraws using LocalSigner after unbonding period', async () => {
81
+ await testStaker.approve(AMOUNT)
82
+ await testStaker.stake(AMOUNT, 0n)
83
+
84
+ const stakeBefore = await testStaker.staker.getStake({
85
+ delegatorAddress: testStaker.delegatorAddress,
86
+ validatorShareAddress: testStaker.validatorShareAddress
87
+ })
88
+ await testStaker.unstake(AMOUNT, stakeBefore.shares)
89
+
90
+ const nonce = await testStaker.staker.getUnbondNonce({
91
+ delegatorAddress: testStaker.delegatorAddress,
92
+ validatorShareAddress: testStaker.validatorShareAddress
93
+ })
94
+
95
+ const unbond = await testStaker.staker.getUnbond({
96
+ delegatorAddress: testStaker.delegatorAddress,
97
+ validatorShareAddress: testStaker.validatorShareAddress,
98
+ unbondNonce: nonce
99
+ })
100
+
101
+ const withdrawalDelay = await getWithdrawalDelay({ publicClient })
102
+ await advanceEpoch({
103
+ publicClient,
104
+ staker: testStaker.staker,
105
+ targetEpoch: unbond.withdrawEpoch + withdrawalDelay
106
+ })
107
+
108
+ const balanceBefore = await getStakingTokenBalance({
109
+ publicClient,
110
+ address: testStaker.delegatorAddress
111
+ })
112
+
113
+ await testStaker.withdraw(nonce)
114
+
115
+ const balanceAfter = await getStakingTokenBalance({
116
+ publicClient,
117
+ address: testStaker.delegatorAddress
118
+ })
119
+ assert.equal(balanceAfter - balanceBefore, parseEther(AMOUNT))
120
+ })
121
+
122
+ it('reports transaction status correctly', async () => {
123
+ const txHash = await testStaker.approve(AMOUNT)
124
+
125
+ const { status } = await testStaker.staker.getTxStatus({ txHash: txHash as `0x${string}` })
126
+ assert.equal(status, 'success')
127
+ })
128
+ })
@@ -0,0 +1,41 @@
1
+ import { createPublicClient, http } from 'viem'
2
+ import { hardhat } from 'viem/chains'
3
+
4
+ let currentSnapshotId: string | null = null
5
+
6
+ const publicClient = createPublicClient({
7
+ chain: hardhat,
8
+ transport: http(hardhat.rpcUrls.default.http[0])
9
+ })
10
+
11
+ before(async () => {
12
+ currentSnapshotId = (await publicClient.request({
13
+ method: 'evm_snapshot',
14
+ params: []
15
+ } as any)) as string
16
+ })
17
+
18
+ export const restoreToInitialState = async () => {
19
+ if (!currentSnapshotId) {
20
+ throw new Error('No snapshot available to restore to')
21
+ }
22
+
23
+ const success = await publicClient.request({
24
+ method: 'evm_revert',
25
+ params: [currentSnapshotId]
26
+ } as any)
27
+
28
+ if (!success) {
29
+ throw new Error('Failed to restore snapshot')
30
+ }
31
+
32
+ const newSnapshotId = (await publicClient.request({
33
+ method: 'evm_snapshot',
34
+ params: []
35
+ } as any)) as string
36
+
37
+ if (!newSnapshotId) {
38
+ throw new Error('Failed to take new snapshot after restore')
39
+ }
40
+ currentSnapshotId = newSnapshotId
41
+ }