@exodus/ethereum-api 8.19.0 → 8.20.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,30 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [8.20.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.19.1...@exodus/ethereum-api@8.20.0) (2024-10-09)
7
+
8
+
9
+ ### Features
10
+
11
+ * **ethereum-api:** use exodus/crypto instead of js-sha3 for ens ([#4147](https://github.com/ExodusMovement/assets/issues/4147)) ([8b46675](https://github.com/ExodusMovement/assets/commit/8b466751247f92f950fecf1d36adddd876091d75))
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * hardening private methods ([#4165](https://github.com/ExodusMovement/assets/issues/4165)) ([cb04b39](https://github.com/ExodusMovement/assets/commit/cb04b39ee3a70abf9a13697e0998e1d55196aac7))
17
+ * staking splitIn32BytesArray hardening and improvements ([#4170](https://github.com/ExodusMovement/assets/issues/4170)) ([2a178ae](https://github.com/ExodusMovement/assets/commit/2a178aefc590186ee699e746d8e71bac1ab8198e))
18
+
19
+
20
+
21
+ ## [8.19.1](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.19.0...@exodus/ethereum-api@8.19.1) (2024-09-29)
22
+
23
+
24
+ ### Bug Fixes
25
+
26
+ * ethereum-api bump asset-lib dep ([#4021](https://github.com/ExodusMovement/assets/issues/4021)) ([a4e1fe9](https://github.com/ExodusMovement/assets/commit/a4e1fe9cb4067b6f7a9e73b21b1602a183b77baa))
27
+
28
+
29
+
6
30
  ## [8.19.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.18.3...@exodus/ethereum-api@8.19.0) (2024-09-26)
7
31
 
8
32
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/ethereum-api",
3
- "version": "8.19.0",
3
+ "version": "8.20.0",
4
4
  "description": "Ethereum Api",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -22,11 +22,11 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@exodus/asset": "^2.0.0",
25
- "@exodus/asset-lib": "^5.0.0",
25
+ "@exodus/asset-lib": "^5.1.0",
26
26
  "@exodus/assets": "^11.0.0",
27
27
  "@exodus/basic-utils": "^3.0.1",
28
28
  "@exodus/bip44-constants": "^195.0.0",
29
- "@exodus/crypto": "^1.0.0-rc.0",
29
+ "@exodus/crypto": "^1.0.0-rc.13",
30
30
  "@exodus/currency": "^5.0.2",
31
31
  "@exodus/ethereum-lib": "^5.4.0",
32
32
  "@exodus/ethereum-meta": "^2.0.0",
@@ -40,7 +40,6 @@
40
40
  "bn.js": "^5.2.1",
41
41
  "events": "^1.1.1",
42
42
  "idna-uts46-hx": "^2.3.1",
43
- "js-sha3": "^0.8.0",
44
43
  "lodash": "^4.17.15",
45
44
  "make-concurrent": "^4.0.0",
46
45
  "minimalistic-assert": "^1.0.1",
@@ -65,5 +64,5 @@
65
64
  "type": "git",
66
65
  "url": "git+https://github.com/ExodusMovement/assets.git"
67
66
  },
68
- "gitHead": "5ed11ccb5d6268dad10582ffd1df7aeae6993f09"
67
+ "gitHead": "a3826445e762843c0bccc5d9f94ecbb9dfe9648d"
69
68
  }
package/src/ens/index.js CHANGED
@@ -1,12 +1,11 @@
1
+ import { keccak256 } from '@exodus/crypto/keccak'
1
2
  import { ABI } from '@exodus/ethereum-lib'
2
3
  import SolidityContract from '@exodus/solidity-contract'
3
4
  import idna from 'idna-uts46-hx'
4
- import jsSha3 from 'js-sha3'
5
5
 
6
6
  import ENS_REGISTRY_ADDRESSES from './addresses.js'
7
7
 
8
8
  const { toUnicode } = idna
9
- const { keccak256 } = jsSha3
10
9
 
11
10
  const registry = new SolidityContract(ABI.ensRegistry)
12
11
  const resolver = new SolidityContract(ABI.ensResolver)
@@ -20,24 +19,16 @@ function namehash(inputName) {
20
19
  // Implementation based on @ensdomains/eth-ens-namehash package
21
20
 
22
21
  // Reject empty names:
23
- var node = ''
24
- var i
25
- for (i = 0; i < 32; i++) {
26
- node += '00'
27
- }
28
-
29
- var name = normalize(inputName)
22
+ let node = Buffer.alloc(32)
30
23
 
24
+ const name = normalize(inputName)
31
25
  if (name) {
32
- var labels = name.split('.')
33
-
34
- for (i = labels.length - 1; i >= 0; i--) {
35
- var labelSha = keccak256(labels[i])
36
- node = keccak256(Buffer.from(node + labelSha, 'hex'))
26
+ for (const label of name.split('.').reverse()) {
27
+ node = keccak256([node, keccak256(label)])
37
28
  }
38
29
  }
39
30
 
40
- return '0x' + node
31
+ return '0x' + node.toString('hex')
41
32
  }
42
33
 
43
34
  // ENS only available on mainnet atm
@@ -1,8 +1,40 @@
1
+ import BN from 'bn.js'
2
+
1
3
  export function fromHexToString(value) {
2
4
  return fromHexToBigInt(value).toString()
3
5
  }
4
6
 
5
7
  export function fromHexToBigInt(value) {
6
- const hex = value.startsWith('0x') ? value : '0x' + value
7
- return BigInt(hex)
8
+ return BigInt(addHexPrefix(value))
9
+ }
10
+
11
+ export const addHexPrefix = (str) => {
12
+ if (typeof str !== 'string') {
13
+ return str
14
+ }
15
+
16
+ return str.startsWith('0x') ? str : '0x' + str
17
+ }
18
+
19
+ export const removeHexPrefix = (str) => {
20
+ if (typeof str !== 'string' || str === '') {
21
+ return str
22
+ }
23
+
24
+ return str.startsWith('0x') ? str.slice(2) : str
25
+ }
26
+
27
+ export const splitIn32BytesArray = (output) => {
28
+ const trimmed = removeHexPrefix(output)
29
+ const result = trimmed.match(/^([\da-f]{64})+$/gi)
30
+ if (result) {
31
+ return trimmed.match(/.{64}/g)
32
+ }
33
+
34
+ throw new Error(`${output} is not a 64 bytes hex that can be split`)
8
35
  }
36
+
37
+ export const removeLeadingZeroes = (str) => str.replace(/^0+/, '')
38
+
39
+ // Deprecated, use BigInt
40
+ export const fromHexToBN = (str) => new BN(removeLeadingZeroes(removeHexPrefix(str)), 16)
@@ -48,12 +48,12 @@ export class EthereumStaking {
48
48
  this.server = server
49
49
  }
50
50
 
51
- buildTxData = (contract, method, ...args) => {
51
+ #buildTxData = (contract, method, ...args) => {
52
52
  return contract[method].build(...args)
53
53
  }
54
54
 
55
- callReadFunctionContract = (contract, method, ...args) => {
56
- const callData = this.buildTxData(contract, method, ...args)
55
+ #callReadFunctionContract = (contract, method, ...args) => {
56
+ const callData = this.#buildTxData(contract, method, ...args)
57
57
  const data = {
58
58
  data: bufferToHex(callData),
59
59
  to: contract.address,
@@ -68,13 +68,16 @@ export class EthereumStaking {
68
68
 
69
69
  /** Return total deposited and activated pool balance */
70
70
  async poolsBalance() {
71
- const totalPoolBalance = await this.callReadFunctionContract(this.contractAccounting, 'balance')
71
+ const totalPoolBalance = await this.#callReadFunctionContract(
72
+ this.contractAccounting,
73
+ 'balance'
74
+ )
72
75
  return this.asset.currency.baseUnit(totalPoolBalance)
73
76
  }
74
77
 
75
78
  /** Return pool pending balance. Always < 32 ETH */
76
79
  async poolPendingBalance() {
77
- const pendingBalance = await this.callReadFunctionContract(
80
+ const pendingBalance = await this.#callReadFunctionContract(
78
81
  this.contractAccounting,
79
82
  'pendingBalance'
80
83
  )
@@ -83,7 +86,7 @@ export class EthereumStaking {
83
86
 
84
87
  /** Return user pending balance (waiting to be added from the Pool contract to the ETH2 validator) */
85
88
  async pendingBalanceOf(address) {
86
- let pendingBalance = await this.callReadFunctionContract(
89
+ let pendingBalance = await this.#callReadFunctionContract(
87
90
  this.contractAccounting,
88
91
  'pendingBalanceOf',
89
92
  address
@@ -94,7 +97,7 @@ export class EthereumStaking {
94
97
 
95
98
  /** Return total user autocompound balance. Part of this balance could be in pending state after rewards autocompound */
96
99
  async autocompoundBalanceOf(address) {
97
- const userBalance = await this.callReadFunctionContract(
100
+ const userBalance = await this.#callReadFunctionContract(
98
101
  this.contractAccounting,
99
102
  'autocompoundBalanceOf',
100
103
  address
@@ -104,7 +107,7 @@ export class EthereumStaking {
104
107
 
105
108
  /** Return user pending deposited balance. Balance which deposited into validator but not active yet. Pending deposited balance can't be unstake till validator activation */
106
109
  async pendingDepositedBalanceOf(address) {
107
- const userBalance = await this.callReadFunctionContract(
110
+ const userBalance = await this.#callReadFunctionContract(
108
111
  this.contractAccounting,
109
112
  'pendingDepositedBalanceOf',
110
113
  address
@@ -114,7 +117,7 @@ export class EthereumStaking {
114
117
 
115
118
  /** Return user active origin deposited balance */
116
119
  async depositedBalanceOf(address) {
117
- const depositedBalance = await this.callReadFunctionContract(
120
+ const depositedBalance = await this.#callReadFunctionContract(
118
121
  this.contractAccounting,
119
122
  'depositedBalanceOf',
120
123
  address
@@ -133,7 +136,7 @@ export class EthereumStaking {
133
136
 
134
137
  /** Return withdrawable unstaked amount */
135
138
  async withdrawRequest(address) {
136
- let amounts = await this.callReadFunctionContract(
139
+ let amounts = await this.#callReadFunctionContract(
137
140
  this.contractAccounting,
138
141
  'withdrawRequest',
139
142
  address
@@ -153,7 +156,7 @@ export class EthereumStaking {
153
156
  return {
154
157
  to: this.poolAddress,
155
158
  amount,
156
- data: bufferToHex(this.buildTxData(this.contractPool, 'stake', source)), // tx data field
159
+ data: bufferToHex(this.#buildTxData(this.contractPool, 'stake', source)), // tx data field
157
160
  }
158
161
  }
159
162
 
@@ -170,7 +173,7 @@ export class EthereumStaking {
170
173
  to: this.poolAddress,
171
174
  amount: this.asset.currency.ZERO,
172
175
  data: bufferToHex(
173
- this.buildTxData(this.contractPool, 'unstake', amountWei, allowedInterchangeNum, source)
176
+ this.#buildTxData(this.contractPool, 'unstake', amountWei, allowedInterchangeNum, source)
174
177
  ),
175
178
  }
176
179
  }
@@ -186,7 +189,7 @@ export class EthereumStaking {
186
189
  return {
187
190
  to: this.accountingAddress,
188
191
  amount: this.asset.currency.ZERO,
189
- data: bufferToHex(this.buildTxData(this.contractAccounting, 'claimWithdrawRequest')),
192
+ data: bufferToHex(this.#buildTxData(this.contractAccounting, 'claimWithdrawRequest')),
190
193
  }
191
194
  }
192
195
 
@@ -206,7 +209,7 @@ export class EthereumStaking {
206
209
  return {
207
210
  to: this.poolAddress,
208
211
  amount: this.asset.currency.ZERO,
209
- data: bufferToHex(this.buildTxData(this.contractPool, 'unstakePending', amountWei)),
212
+ data: bufferToHex(this.#buildTxData(this.contractPool, 'unstakePending', amountWei)),
210
213
  }
211
214
  } catch (err) {
212
215
  throw new Error(err)
@@ -7,7 +7,7 @@ export class FantomStaking {
7
7
  this.sfcContract = createContract(SFC_ADDRESS, 'fantomSfc')
8
8
  }
9
9
 
10
- buildTxData = (contract, method, ...args) => {
10
+ #buildTxData = (contract, method, ...args) => {
11
11
  return contract[method].build(...args)
12
12
  }
13
13
 
@@ -16,7 +16,7 @@ export class FantomStaking {
16
16
  */
17
17
  delegate = ({ validatorId }) => {
18
18
  this.#validateValidatorId(validatorId)
19
- return this.buildTxData(this.sfcContract, 'delegate', validatorId)
19
+ return this.#buildTxData(this.sfcContract, 'delegate', validatorId)
20
20
  }
21
21
 
22
22
  /**
@@ -31,7 +31,7 @@ export class FantomStaking {
31
31
  undelegate = ({ requestId, validatorId, amount }) => {
32
32
  this.#validateRequestId(requestId)
33
33
  this.#validateValidatorId(validatorId)
34
- return this.buildTxData(this.sfcContract, 'undelegate', validatorId, requestId, amount)
34
+ return this.#buildTxData(this.sfcContract, 'undelegate', validatorId, requestId, amount)
35
35
  }
36
36
 
37
37
  /**
@@ -44,7 +44,7 @@ export class FantomStaking {
44
44
  withdraw = ({ requestId, validatorId }) => {
45
45
  this.#validateRequestId(requestId)
46
46
  this.#validateValidatorId(validatorId)
47
- return this.buildTxData(this.sfcContract, 'withdraw', validatorId, requestId)
47
+ return this.#buildTxData(this.sfcContract, 'withdraw', validatorId, requestId)
48
48
  }
49
49
 
50
50
  /**
@@ -53,7 +53,7 @@ export class FantomStaking {
53
53
  */
54
54
  claimRewards = ({ validatorId }) => {
55
55
  this.#validateValidatorId(validatorId)
56
- return this.buildTxData(this.sfcContract, 'claimRewards', validatorId)
56
+ return this.#buildTxData(this.sfcContract, 'claimRewards', validatorId)
57
57
  }
58
58
 
59
59
  /**
@@ -62,7 +62,7 @@ export class FantomStaking {
62
62
  */
63
63
  restakeRewards = ({ validatorId }) => {
64
64
  this.#validateValidatorId(validatorId)
65
- return this.buildTxData(this.sfcContract, 'restakeRewards', validatorId)
65
+ return this.#buildTxData(this.sfcContract, 'restakeRewards', validatorId)
66
66
  }
67
67
 
68
68
  /**
@@ -71,7 +71,7 @@ export class FantomStaking {
71
71
  lockStake = ({ validatorId, amount, durationSeconds }) => {
72
72
  this.#validateValidatorId(validatorId)
73
73
  this.#validateDuration(durationSeconds)
74
- return this.buildTxData(this.sfcContract, 'lockStake', validatorId, durationSeconds, amount)
74
+ return this.#buildTxData(this.sfcContract, 'lockStake', validatorId, durationSeconds, amount)
75
75
  }
76
76
 
77
77
  /**
@@ -80,7 +80,7 @@ export class FantomStaking {
80
80
  relockStake = ({ validatorId, amount, durationSeconds }) => {
81
81
  this.#validateValidatorId(validatorId)
82
82
  this.#validateDuration(durationSeconds)
83
- return this.buildTxData(this.sfcContract, 'relockStake', validatorId, durationSeconds, amount)
83
+ return this.#buildTxData(this.sfcContract, 'relockStake', validatorId, durationSeconds, amount)
84
84
  }
85
85
 
86
86
  /**
@@ -88,7 +88,7 @@ export class FantomStaking {
88
88
  */
89
89
  unlockStake = ({ validatorId, amount }) => {
90
90
  this.#validateValidatorId(validatorId)
91
- return this.buildTxData(this.sfcContract, 'unlockStake', validatorId, amount)
91
+ return this.#buildTxData(this.sfcContract, 'unlockStake', validatorId, amount)
92
92
  }
93
93
 
94
94
  #validateValidatorId = (validatorId) => {
@@ -5,6 +5,7 @@ import { retry } from '@exodus/simple-retry'
5
5
  import BN from 'bn.js'
6
6
 
7
7
  import { getServerByName } from '../../exodus-eth-server/index.js'
8
+ import { fromHexToBN, fromHexToString, splitIn32BytesArray } from '../../number-utils.js'
8
9
 
9
10
  const polygonEthToken = ethAssets.find(({ name: tokenName }) => tokenName === 'polygon')
10
11
 
@@ -27,12 +28,12 @@ export class MaticStakingApi {
27
28
  this.server = server
28
29
  }
29
30
 
30
- buildTxData = (contract, method, ...args) => {
31
+ #buildTxData = (contract, method, ...args) => {
31
32
  return contract[method].build(...args)
32
33
  }
33
34
 
34
- callReadFunctionContract = (contract, method, ...args) => {
35
- const callData = this.buildTxData(contract, method, ...args)
35
+ #callReadFunctionContract = (contract, method, ...args) => {
36
+ const callData = this.#buildTxData(contract, method, ...args)
36
37
  const data = {
37
38
  data: bufferToHex(callData),
38
39
  to: contract.address,
@@ -44,15 +45,15 @@ export class MaticStakingApi {
44
45
  }
45
46
 
46
47
  getWithdrawalDelay = async () => {
47
- const withdrawalDelay = await this.callReadFunctionContract(
48
+ const withdrawalDelay = await this.#callReadFunctionContract(
48
49
  this.stakingManagerContract,
49
50
  'withdrawalDelay'
50
51
  )
51
- return toBN(withdrawalDelay)
52
+ return fromHexToBN(withdrawalDelay)
52
53
  }
53
54
 
54
55
  getMinRewardsToWithdraw = async () => {
55
- const minRewardsToWithdraw = await this.callReadFunctionContract(
56
+ const minRewardsToWithdraw = await this.#callReadFunctionContract(
56
57
  this.validatorShareContract,
57
58
  'minAmount'
58
59
  )
@@ -64,8 +65,8 @@ export class MaticStakingApi {
64
65
  * stored in a list and points the events that happened in that epoch. (timeline)
65
66
  */
66
67
  getCurrentCheckpoint = async () => {
67
- const currentEpoch = await this.callReadFunctionContract(this.stakingManagerContract, 'epoch')
68
- return toBN(currentEpoch)
68
+ const currentEpoch = await this.#callReadFunctionContract(this.stakingManagerContract, 'epoch')
69
+ return fromHexToBN(currentEpoch)
69
70
  }
70
71
 
71
72
  /**
@@ -79,7 +80,7 @@ export class MaticStakingApi {
79
80
  _nonce = await this.getCurrentUnbondNonce(address)
80
81
  }
81
82
 
82
- const unboundInfo = await this.callReadFunctionContract(
83
+ const unboundInfo = await this.#callReadFunctionContract(
83
84
  this.validatorShareContract,
84
85
  'unbonds_new',
85
86
  address,
@@ -88,8 +89,8 @@ export class MaticStakingApi {
88
89
 
89
90
  const [shares, withdrawEpoch] = splitIn32BytesArray(unboundInfo)
90
91
  return {
91
- withdrawEpoch: toBN(withdrawEpoch),
92
- shares: toBN(shares),
92
+ withdrawEpoch: fromHexToBN(withdrawEpoch),
93
+ shares: fromHexToBN(shares),
93
94
  }
94
95
  }
95
96
 
@@ -99,7 +100,7 @@ export class MaticStakingApi {
99
100
  * @returns current unbonded nonce
100
101
  */
101
102
  getCurrentUnbondNonce = async (address) => {
102
- const unbondNonce = await this.callReadFunctionContract(
103
+ const unbondNonce = await this.#callReadFunctionContract(
103
104
  this.validatorShareContract,
104
105
  'unbondNonces',
105
106
  address
@@ -108,7 +109,7 @@ export class MaticStakingApi {
108
109
  }
109
110
 
110
111
  getLiquidRewards = async (address) => {
111
- const liquidRewards = await this.callReadFunctionContract(
112
+ const liquidRewards = await this.#callReadFunctionContract(
112
113
  this.validatorShareContract,
113
114
  'getLiquidRewards',
114
115
  address
@@ -117,22 +118,22 @@ export class MaticStakingApi {
117
118
  }
118
119
 
119
120
  getTotalStake = async (address) => {
120
- const stakeInfo = await this.callReadFunctionContract(
121
+ const stakeInfo = await this.#callReadFunctionContract(
121
122
  this.validatorShareContract,
122
123
  'getTotalStake',
123
124
  address
124
125
  )
125
126
  const [amount] = splitIn32BytesArray(stakeInfo)
126
- return polygonEthToken.currency.baseUnit(toBN(amount).toString())
127
+ return polygonEthToken.currency.baseUnit(fromHexToString(amount))
127
128
  }
128
129
 
129
130
  getWithdrawExchangeRate = async () => {
130
- const withdrawExchangeRate = await this.callReadFunctionContract(
131
+ const withdrawExchangeRate = await this.#callReadFunctionContract(
131
132
  this.validatorShareContract,
132
133
  'withdrawExchangeRate'
133
134
  )
134
135
 
135
- return toBN(withdrawExchangeRate)
136
+ return fromHexToBN(withdrawExchangeRate)
136
137
  }
137
138
 
138
139
  /**
@@ -145,7 +146,7 @@ export class MaticStakingApi {
145
146
  * @param amount Matic tokens to be approved for StakeManager to withdraw (polygon currency)
146
147
  */
147
148
  approveStakeManager = (amount) => {
148
- return this.buildTxData(
149
+ return this.#buildTxData(
149
150
  this.polygonContract,
150
151
  'increaseAllowance',
151
152
  this.stakingManagerContract.address,
@@ -154,20 +155,20 @@ export class MaticStakingApi {
154
155
  }
155
156
 
156
157
  restakeReward = () => {
157
- return this.buildTxData(this.validatorShareContract, 'restake')
158
+ return this.#buildTxData(this.validatorShareContract, 'restake')
158
159
  }
159
160
 
160
161
  withdrawRewards = () => {
161
- return this.buildTxData(this.validatorShareContract, 'withdrawRewards')
162
+ return this.#buildTxData(this.validatorShareContract, 'withdrawRewards')
162
163
  }
163
164
 
164
165
  delegate = ({ amount }) => {
165
- return this.buildTxData(this.validatorShareContract, 'buyVoucher', amount.toBaseString(), '0')
166
+ return this.#buildTxData(this.validatorShareContract, 'buyVoucher', amount.toBaseString(), '0')
166
167
  }
167
168
 
168
169
  undelegate = ({ amount, maximumSharesToBurn }) => {
169
170
  const _maximumSharesToBurn = maximumSharesToBurn || amount
170
- return this.buildTxData(
171
+ return this.#buildTxData(
171
172
  this.validatorShareContract,
172
173
  'sellVoucher_new',
173
174
  amount.toBaseString(),
@@ -179,22 +180,6 @@ export class MaticStakingApi {
179
180
  * @param {number} unbondNonce the unbond nonce from where delegator claim its staked tokens
180
181
  */
181
182
  claimUndelegatedBalance = ({ unbondNonce }) => {
182
- return this.buildTxData(this.validatorShareContract, 'unstakeClaimTokens_new', unbondNonce)
183
+ return this.#buildTxData(this.validatorShareContract, 'unstakeClaimTokens_new', unbondNonce)
183
184
  }
184
185
  }
185
-
186
- // see arguments encoding standard (padded 32 bytes)
187
- // https://docs.soliditylang.org/en/v0.8.15/abi-spec.html?highlight=abi.encode#function-selector-and-argument-encoding
188
- const splitIn32BytesArray = (output) => removeHexPrefix(output).match(/[\da-f]{1,64}/gi) || []
189
-
190
- const toBN = (str) => new BN(removeLeadingZeroes(removeHexPrefix(str)), 16)
191
-
192
- const removeLeadingZeroes = (str) => str.replace(/^0+/, '')
193
-
194
- const removeHexPrefix = (str) => {
195
- if (typeof str !== 'string' || str === '') {
196
- return str
197
- }
198
-
199
- return str.startsWith('0x') ? str.slice(2) : str
200
- }