@exodus/solana-api 2.5.29 → 2.5.30-patch
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/package.json +2 -2
- package/src/get-balances.js +63 -11
- package/src/tx-send.js +54 -74
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/solana-api",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.30-patch",
|
|
4
4
|
"description": "Exodus internal Solana asset API wrapper",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -34,5 +34,5 @@
|
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@exodus/assets-testing": "file:../../../__testing__"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "9ec7084bcdfe14f1c6f11df393351885773046a6"
|
|
38
38
|
}
|
package/src/get-balances.js
CHANGED
|
@@ -1,21 +1,30 @@
|
|
|
1
|
+
import { TxSet } from '@exodus/models'
|
|
2
|
+
|
|
1
3
|
// staking may be a feature that may not be available for a given wallet.
|
|
2
4
|
// In this case, The wallet should exclude the staking balance from the general balance
|
|
3
5
|
|
|
4
|
-
export const getBalancesFactory = ({ stakingFeatureAvailable }) => ({
|
|
6
|
+
export const getBalancesFactory = ({ stakingFeatureAvailable }) => ({
|
|
7
|
+
asset,
|
|
8
|
+
accountState,
|
|
9
|
+
txLog,
|
|
10
|
+
}) => {
|
|
11
|
+
const zero = asset.currency.ZERO
|
|
12
|
+
const { balance, locked, withdrawable, pending } = fixBalances({
|
|
13
|
+
txLog,
|
|
14
|
+
balance: getBalanceFromAccountState({ asset, accountState }),
|
|
15
|
+
locked: accountState.mem?.locked || zero,
|
|
16
|
+
withdrawable: accountState.mem?.withdrawable || zero,
|
|
17
|
+
pending: accountState.mem?.pending || zero,
|
|
18
|
+
asset,
|
|
19
|
+
})
|
|
5
20
|
if (asset.baseAsset.name !== asset.name) {
|
|
6
|
-
return { balance
|
|
21
|
+
return { balance, spendableBalance: balance }
|
|
7
22
|
}
|
|
8
23
|
|
|
9
|
-
const zero = asset.currency.ZERO
|
|
10
|
-
|
|
11
|
-
const balance = accountState.balance || zero
|
|
12
|
-
|
|
13
|
-
const { locked, withdrawable, pending } = accountState.mem || Object.create(null)
|
|
14
|
-
|
|
15
24
|
const balanceWithoutStaking = balance
|
|
16
|
-
.sub(locked
|
|
17
|
-
.sub(withdrawable
|
|
18
|
-
.sub(pending
|
|
25
|
+
.sub(locked)
|
|
26
|
+
.sub(withdrawable)
|
|
27
|
+
.sub(pending)
|
|
19
28
|
.clampLowerZero()
|
|
20
29
|
|
|
21
30
|
return {
|
|
@@ -23,3 +32,46 @@ export const getBalancesFactory = ({ stakingFeatureAvailable }) => ({ asset, acc
|
|
|
23
32
|
spendableBalance: balanceWithoutStaking.sub(asset.accountReserve || zero).clampLowerZero(),
|
|
24
33
|
}
|
|
25
34
|
}
|
|
35
|
+
|
|
36
|
+
const fixBalances = ({ txLog = TxSet.EMPTY, balance, locked, withdrawable, pending, asset }) => {
|
|
37
|
+
for (const tx of txLog) {
|
|
38
|
+
if ((tx.sent || tx.data.staking) && tx.pending && !tx.error) {
|
|
39
|
+
if (tx.coinAmount.unitType.equals(tx.feeAmount.unitType)) {
|
|
40
|
+
balance = balance.sub(tx.feeAmount)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!tx.data.staking) {
|
|
44
|
+
// coinAmount is negative for sent tx
|
|
45
|
+
balance = balance.sub(tx.coinAmount.abs())
|
|
46
|
+
} else {
|
|
47
|
+
// staking tx
|
|
48
|
+
switch (tx.data.staking?.method) {
|
|
49
|
+
case 'delegate':
|
|
50
|
+
locked = locked.add(tx.coinAmount.abs())
|
|
51
|
+
break
|
|
52
|
+
case 'withdraw':
|
|
53
|
+
withdrawable = asset.currency.ZERO
|
|
54
|
+
break
|
|
55
|
+
case 'undelegate':
|
|
56
|
+
pending = pending.add(locked)
|
|
57
|
+
locked = asset.currency.ZERO
|
|
58
|
+
break
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
balance: balance.clampLowerZero(),
|
|
65
|
+
locked: locked.clampLowerZero(),
|
|
66
|
+
withdrawable: withdrawable.clampLowerZero(),
|
|
67
|
+
pending: pending.clampLowerZero(),
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const getBalanceFromAccountState = ({ asset, accountState }) => {
|
|
72
|
+
const isBase = asset.name === asset.baseAsset.name
|
|
73
|
+
return (
|
|
74
|
+
(isBase ? accountState?.balance : accountState?.tokenBalances?.[asset.name]) ||
|
|
75
|
+
asset.currency.ZERO
|
|
76
|
+
)
|
|
77
|
+
}
|
package/src/tx-send.js
CHANGED
|
@@ -10,7 +10,6 @@ export const createAndBroadcastTXFactory = (api) => async (
|
|
|
10
10
|
|
|
11
11
|
const {
|
|
12
12
|
feeAmount,
|
|
13
|
-
shouldLog = true,
|
|
14
13
|
method,
|
|
15
14
|
stakeAddresses,
|
|
16
15
|
seed,
|
|
@@ -33,21 +32,37 @@ export const createAndBroadcastTXFactory = (api) => async (
|
|
|
33
32
|
reference,
|
|
34
33
|
memo,
|
|
35
34
|
} = options
|
|
36
|
-
let { recentBlockhash } = options
|
|
37
35
|
const { baseAsset } = asset
|
|
38
36
|
const from = await assetClientInterface.getReceiveAddress({
|
|
39
37
|
assetName: baseAsset.name,
|
|
40
38
|
walletAccount,
|
|
41
39
|
})
|
|
42
|
-
const currentAccountState = await assetClientInterface.getAccountState({
|
|
43
|
-
assetName: baseAsset.name,
|
|
44
|
-
walletAccount,
|
|
45
|
-
})
|
|
46
40
|
|
|
47
|
-
|
|
41
|
+
const isToken = asset.assetType === 'SOLANA_TOKEN'
|
|
42
|
+
|
|
43
|
+
// Check if receiver has address active when sending tokens.
|
|
44
|
+
if (isToken) {
|
|
45
|
+
// check address mint is the same
|
|
46
|
+
const targetMint = await api.getAddressMint(address) // null if it's a SOL address
|
|
47
|
+
if (targetMint && targetMint !== asset.mintAddress) {
|
|
48
|
+
const err = new Error('Wrong Destination Wallet')
|
|
49
|
+
err.reason = { mintAddressMismatch: true }
|
|
50
|
+
throw err
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
// sending SOL
|
|
54
|
+
const addressType = await api.getAddressType(address)
|
|
55
|
+
if (addressType === 'token') {
|
|
56
|
+
const err = new Error('Destination Wallet is a Token address')
|
|
57
|
+
err.reason = { wrongAddressType: true }
|
|
58
|
+
throw err
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const recentBlockhash = options.recentBlockhash || (await api.getRecentBlockHash())
|
|
48
63
|
|
|
49
64
|
let tokenParams = Object.create(null)
|
|
50
|
-
if (
|
|
65
|
+
if (isToken || customMintAddress) {
|
|
51
66
|
const tokenMintAddress = customMintAddress || asset.mintAddress
|
|
52
67
|
const tokenAddress = findAssociatedTokenAddress(address, tokenMintAddress)
|
|
53
68
|
const [
|
|
@@ -95,7 +110,7 @@ export const createAndBroadcastTXFactory = (api) => async (
|
|
|
95
110
|
creators,
|
|
96
111
|
}
|
|
97
112
|
|
|
98
|
-
const unsignedTransaction =
|
|
113
|
+
const unsignedTransaction = createUnsignedTx({
|
|
99
114
|
asset,
|
|
100
115
|
from,
|
|
101
116
|
to: address,
|
|
@@ -118,38 +133,36 @@ export const createAndBroadcastTXFactory = (api) => async (
|
|
|
118
133
|
await baseAsset.api.broadcastTx(rawTx)
|
|
119
134
|
|
|
120
135
|
const selfSend = from === address
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
const isStakingTx = ['delegate', 'undelegate', 'withdraw'].includes(method)
|
|
137
|
+
const coinAmount = isStakingTx
|
|
138
|
+
? amount.abs()
|
|
139
|
+
: selfSend
|
|
140
|
+
? asset.currency.ZERO
|
|
141
|
+
: amount.abs().negate()
|
|
142
|
+
|
|
143
|
+
const data = isStakingTx
|
|
144
|
+
? { staking: { ...stakingParams, stake: coinAmount.toBaseNumber() } }
|
|
145
|
+
: Object.create(null)
|
|
146
|
+
|
|
147
|
+
const tx = {
|
|
148
|
+
txId,
|
|
149
|
+
confirmations: 0,
|
|
150
|
+
coinName: assetName,
|
|
151
|
+
coinAmount,
|
|
152
|
+
feeAmount,
|
|
153
|
+
feeCoinName: asset.feeAsset.name,
|
|
154
|
+
selfSend,
|
|
155
|
+
to: address,
|
|
156
|
+
data,
|
|
157
|
+
currencies: { [assetName]: asset.currency, [asset.feeAsset.name]: asset.feeAsset.currency },
|
|
139
158
|
}
|
|
159
|
+
await assetClientInterface.updateTxLogAndNotify({ assetName, walletAccount, txs: [tx] })
|
|
140
160
|
|
|
141
|
-
|
|
142
|
-
if (asset.assetType === 'SOLANA_TOKEN') {
|
|
143
|
-
Object.assign(changes, {
|
|
144
|
-
balance: currentAccountState.balance.sub(feeAmount), // solana balance
|
|
145
|
-
tokenBalances: {
|
|
146
|
-
...currentAccountState.tokenBalances,
|
|
147
|
-
[assetName]: currentAccountState.tokenBalances[assetName].sub(coinAmount.abs()),
|
|
148
|
-
}, // SPL token balance
|
|
149
|
-
})
|
|
161
|
+
if (isToken) {
|
|
150
162
|
// write tx entry in solana for token fee
|
|
151
|
-
const
|
|
163
|
+
const txForFee = {
|
|
152
164
|
txId,
|
|
165
|
+
confirmations: 0,
|
|
153
166
|
coinName: baseAsset.name,
|
|
154
167
|
coinAmount: baseAsset.currency.ZERO,
|
|
155
168
|
tokens: [assetName],
|
|
@@ -162,45 +175,12 @@ export const createAndBroadcastTXFactory = (api) => async (
|
|
|
162
175
|
[baseAsset.feeAsset.name]: baseAsset.feeAsset.currency,
|
|
163
176
|
},
|
|
164
177
|
}
|
|
165
|
-
await assetClientInterface.updateTxLogAndNotify({
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
})
|
|
170
|
-
} else if (method) {
|
|
171
|
-
// staking: no changes
|
|
172
|
-
} else {
|
|
173
|
-
// SOL transfer
|
|
174
|
-
Object.assign(changes, {
|
|
175
|
-
balance: currentAccountState.balance.sub(coinAmount.abs()).sub(feeAmount),
|
|
178
|
+
await assetClientInterface.updateTxLogAndNotify({
|
|
179
|
+
assetName: baseAsset.name,
|
|
180
|
+
walletAccount,
|
|
181
|
+
txs: [txForFee],
|
|
176
182
|
})
|
|
177
183
|
}
|
|
178
184
|
|
|
179
|
-
if (method) {
|
|
180
|
-
const stakingData = { ...currentAccountState.mem }
|
|
181
|
-
switch (method) {
|
|
182
|
-
case 'delegate':
|
|
183
|
-
stakingData.isDelegating = true
|
|
184
|
-
stakingData.locked = stakingData.locked.add(amount)
|
|
185
|
-
break
|
|
186
|
-
case 'undelegate':
|
|
187
|
-
stakingData.isDelegating = false
|
|
188
|
-
stakingData.pending = stakingData.pending.add(stakingData.locked)
|
|
189
|
-
stakingData.locked = asset.currency.ZERO
|
|
190
|
-
break
|
|
191
|
-
case 'withdraw':
|
|
192
|
-
stakingData.withdrawable = asset.currency.ZERO
|
|
193
|
-
break
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
Object.assign(changes, { mem: stakingData })
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
await assetClientInterface.updateAccountState({
|
|
200
|
-
assetName: baseAsset.name,
|
|
201
|
-
walletAccount,
|
|
202
|
-
newData: changes,
|
|
203
|
-
})
|
|
204
|
-
|
|
205
185
|
return { txId }
|
|
206
186
|
}
|