@exodus/solana-api 2.5.29 → 2.5.30
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 +51 -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",
|
|
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": "5270d4cf5f0a0ae9486533f237e6abe6f026ed16"
|
|
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,33 @@ 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 ? { staking: stakingParams } : Object.create(null)
|
|
144
|
+
const tx = {
|
|
145
|
+
txId,
|
|
146
|
+
confirmations: 0,
|
|
147
|
+
coinName: assetName,
|
|
148
|
+
coinAmount,
|
|
149
|
+
feeAmount,
|
|
150
|
+
feeCoinName: asset.feeAsset.name,
|
|
151
|
+
selfSend,
|
|
152
|
+
to: address,
|
|
153
|
+
data,
|
|
154
|
+
currencies: { [assetName]: asset.currency, [asset.feeAsset.name]: asset.feeAsset.currency },
|
|
139
155
|
}
|
|
156
|
+
await assetClientInterface.updateTxLogAndNotify({ assetName, walletAccount, txs: [tx] })
|
|
140
157
|
|
|
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
|
-
})
|
|
158
|
+
if (isToken) {
|
|
150
159
|
// write tx entry in solana for token fee
|
|
151
|
-
const
|
|
160
|
+
const txForFee = {
|
|
152
161
|
txId,
|
|
162
|
+
confirmations: 0,
|
|
153
163
|
coinName: baseAsset.name,
|
|
154
164
|
coinAmount: baseAsset.currency.ZERO,
|
|
155
165
|
tokens: [assetName],
|
|
@@ -162,45 +172,12 @@ export const createAndBroadcastTXFactory = (api) => async (
|
|
|
162
172
|
[baseAsset.feeAsset.name]: baseAsset.feeAsset.currency,
|
|
163
173
|
},
|
|
164
174
|
}
|
|
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),
|
|
175
|
+
await assetClientInterface.updateTxLogAndNotify({
|
|
176
|
+
assetName: baseAsset.name,
|
|
177
|
+
walletAccount,
|
|
178
|
+
txs: [txForFee],
|
|
176
179
|
})
|
|
177
180
|
}
|
|
178
181
|
|
|
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
182
|
return { txId }
|
|
206
183
|
}
|