@meshconnect/uwc-injected-connector 0.2.0 → 0.2.1
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/dist/injected-connector.d.ts +5 -1
- package/dist/injected-connector.d.ts.map +1 -1
- package/dist/injected-connector.js +8 -2
- package/dist/injected-connector.js.map +1 -1
- package/dist/services/connection-manager.d.ts +2 -2
- package/dist/services/connection-manager.d.ts.map +1 -1
- package/dist/services/connection-manager.js +2 -2
- package/dist/services/connection-manager.js.map +1 -1
- package/dist/services/ethereum/ethereum-transaction-builder.d.ts +14 -0
- package/dist/services/ethereum/ethereum-transaction-builder.d.ts.map +1 -0
- package/dist/services/ethereum/ethereum-transaction-builder.js +60 -0
- package/dist/services/ethereum/ethereum-transaction-builder.js.map +1 -0
- package/dist/services/ethereum/ethereum-transaction-service.d.ts +26 -0
- package/dist/services/ethereum/ethereum-transaction-service.d.ts.map +1 -0
- package/dist/services/ethereum/ethereum-transaction-service.js +145 -0
- package/dist/services/ethereum/ethereum-transaction-service.js.map +1 -0
- package/dist/services/ethereum/ethereum-wallet-service.d.ts +59 -0
- package/dist/services/ethereum/ethereum-wallet-service.d.ts.map +1 -0
- package/dist/services/ethereum/ethereum-wallet-service.js +173 -0
- package/dist/services/ethereum/ethereum-wallet-service.js.map +1 -0
- package/dist/services/ethereum-transaction-service.d.ts +3 -0
- package/dist/services/ethereum-transaction-service.d.ts.map +1 -1
- package/dist/services/ethereum-transaction-service.js +83 -34
- package/dist/services/ethereum-transaction-service.js.map +1 -1
- package/dist/services/index.d.ts +3 -3
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +3 -3
- package/dist/services/index.js.map +1 -1
- package/dist/services/solana/solana-transaction-builder.d.ts +20 -0
- package/dist/services/solana/solana-transaction-builder.d.ts.map +1 -0
- package/dist/services/solana/solana-transaction-builder.js +112 -0
- package/dist/services/solana/solana-transaction-builder.js.map +1 -0
- package/dist/services/solana/solana-transaction-service.d.ts +27 -0
- package/dist/services/solana/solana-transaction-service.d.ts.map +1 -0
- package/dist/services/solana/solana-transaction-service.js +136 -0
- package/dist/services/solana/solana-transaction-service.js.map +1 -0
- package/dist/services/solana/solana-wallet-service.d.ts +55 -0
- package/dist/services/solana/solana-wallet-service.d.ts.map +1 -0
- package/dist/services/solana/solana-wallet-service.js +129 -0
- package/dist/services/solana/solana-wallet-service.js.map +1 -0
- package/dist/services/transaction-service.d.ts +1 -13
- package/dist/services/transaction-service.d.ts.map +1 -1
- package/dist/services/transaction-service.js +4 -42
- package/dist/services/transaction-service.js.map +1 -1
- package/package.json +4 -2
- package/src/injected-connector.ts +13 -3
- package/src/services/connection-manager.ts +2 -2
- package/src/services/ethereum/ethereum-transaction-builder.ts +82 -0
- package/src/services/ethereum/ethereum-transaction-service.ts +192 -0
- package/src/services/{ethereum-wallet-service.ts → ethereum/ethereum-wallet-service.ts} +56 -3
- package/src/services/index.ts +3 -3
- package/src/services/solana/solana-transaction-builder.ts +214 -0
- package/src/services/solana/solana-transaction-service.ts +190 -0
- package/src/services/{solana-wallet-service.ts → solana/solana-wallet-service.ts} +2 -2
- package/src/services/transaction-service.ts +9 -67
- package/dist/injected-connector.old.d.ts +0 -44
- package/dist/injected-connector.old.d.ts.map +0 -1
- package/dist/injected-connector.old.js +0 -491
- package/dist/injected-connector.old.js.map +0 -1
- package/dist/providers/evm-provider.d.ts +0 -18
- package/dist/providers/evm-provider.d.ts.map +0 -1
- package/dist/providers/evm-provider.js +0 -86
- package/dist/providers/evm-provider.js.map +0 -1
- package/dist/providers/solana-provider.d.ts +0 -22
- package/dist/providers/solana-provider.d.ts.map +0 -1
- package/dist/providers/solana-provider.js +0 -137
- package/dist/providers/solana-provider.js.map +0 -1
- package/src/services/ethereum-transaction-service.ts +0 -196
- package/src/services/solana-transaction-service.ts +0 -68
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { ethers } from 'ethers'
|
|
2
|
+
import type { EthereumProvider } from '../../eip6963-discovery'
|
|
3
|
+
import type {
|
|
4
|
+
EVMNativeTransferRequest,
|
|
5
|
+
EVMContractCallRequest,
|
|
6
|
+
EVMBatchTransactionRequest,
|
|
7
|
+
BatchTransactionResult,
|
|
8
|
+
EthereumTransactionRequest
|
|
9
|
+
} from '@meshconnect/uwc-types'
|
|
10
|
+
import { parseError } from '../../utils/error-utils'
|
|
11
|
+
import { EthereumTransactionBuilder } from './ethereum-transaction-builder'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Service for handling Ethereum/EVM transaction operations
|
|
15
|
+
*/
|
|
16
|
+
export class EthereumTransactionService {
|
|
17
|
+
private builder: EthereumTransactionBuilder
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
this.builder = new EthereumTransactionBuilder()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Send a transaction for Ethereum
|
|
25
|
+
*/
|
|
26
|
+
async sendTransaction(
|
|
27
|
+
request: EthereumTransactionRequest,
|
|
28
|
+
provider: EthereumProvider
|
|
29
|
+
): Promise<string> {
|
|
30
|
+
try {
|
|
31
|
+
// Handle different types of EVM transactions
|
|
32
|
+
if ('contractAddress' in request && 'abi' in request) {
|
|
33
|
+
// Contract call
|
|
34
|
+
return await this.sendContractCall(
|
|
35
|
+
provider,
|
|
36
|
+
request as EVMContractCallRequest
|
|
37
|
+
)
|
|
38
|
+
} else if ('calls' in request) {
|
|
39
|
+
// Batch transaction
|
|
40
|
+
return await this.sendBatch(
|
|
41
|
+
provider,
|
|
42
|
+
request as EVMBatchTransactionRequest
|
|
43
|
+
)
|
|
44
|
+
} else if ('amount' in request && typeof request.amount === 'bigint') {
|
|
45
|
+
// Native transfer
|
|
46
|
+
return await this.sendNativeTransaction(
|
|
47
|
+
provider,
|
|
48
|
+
request as EVMNativeTransferRequest
|
|
49
|
+
)
|
|
50
|
+
} else {
|
|
51
|
+
throw new Error('Invalid transaction request type')
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
parseError(error)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Send native token transfer (ETH, BNB, MATIC, etc.)
|
|
60
|
+
*/
|
|
61
|
+
private async sendNativeTransaction(
|
|
62
|
+
provider: EthereumProvider,
|
|
63
|
+
request: EVMNativeTransferRequest
|
|
64
|
+
): Promise<string> {
|
|
65
|
+
try {
|
|
66
|
+
// Build transaction
|
|
67
|
+
const tx = await this.builder.getNativeTransaction(request)
|
|
68
|
+
|
|
69
|
+
// Create ethers provider and signer
|
|
70
|
+
const ethersProvider = new ethers.BrowserProvider(provider)
|
|
71
|
+
const signer = await ethersProvider.getSigner(request.from)
|
|
72
|
+
|
|
73
|
+
// Send transaction
|
|
74
|
+
const response = await signer.sendTransaction(tx)
|
|
75
|
+
|
|
76
|
+
// Wait for confirmation
|
|
77
|
+
const receipt = await response.wait()
|
|
78
|
+
return receipt ? receipt.hash : ''
|
|
79
|
+
} catch (error) {
|
|
80
|
+
parseError(error)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Send smart contract transaction (including ERC-20 transfers)
|
|
86
|
+
*/
|
|
87
|
+
private async sendContractCall(
|
|
88
|
+
provider: EthereumProvider,
|
|
89
|
+
request: EVMContractCallRequest
|
|
90
|
+
): Promise<string> {
|
|
91
|
+
try {
|
|
92
|
+
// Build transaction
|
|
93
|
+
const tx = await this.builder.getContractTransaction(request)
|
|
94
|
+
|
|
95
|
+
// Create ethers provider and signer
|
|
96
|
+
const ethersProvider = new ethers.BrowserProvider(provider)
|
|
97
|
+
const signer = await ethersProvider.getSigner(request.from)
|
|
98
|
+
|
|
99
|
+
// Send transaction
|
|
100
|
+
const response = await signer.sendTransaction(tx)
|
|
101
|
+
|
|
102
|
+
// Wait for confirmation
|
|
103
|
+
const receipt = await response.wait()
|
|
104
|
+
return receipt ? receipt.hash : ''
|
|
105
|
+
} catch (error) {
|
|
106
|
+
parseError(error)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Send batch of transactions (EIP-5792)
|
|
112
|
+
*/
|
|
113
|
+
private async sendBatch(
|
|
114
|
+
provider: EthereumProvider,
|
|
115
|
+
request: EVMBatchTransactionRequest
|
|
116
|
+
): Promise<string> {
|
|
117
|
+
try {
|
|
118
|
+
// Prepare calls
|
|
119
|
+
const calls = await Promise.all(
|
|
120
|
+
request.calls.map(async tx => {
|
|
121
|
+
if ('contractAddress' in tx && 'abi' in tx) {
|
|
122
|
+
// Contract call
|
|
123
|
+
const result = await this.builder.getContractTransaction(
|
|
124
|
+
tx as EVMContractCallRequest
|
|
125
|
+
)
|
|
126
|
+
return {
|
|
127
|
+
to: result.to ?? '',
|
|
128
|
+
value: '0x' + (result.value ?? 0n).toString(16),
|
|
129
|
+
data: result.data
|
|
130
|
+
}
|
|
131
|
+
} else if ('amount' in tx && typeof tx.amount === 'bigint') {
|
|
132
|
+
// Native transfer
|
|
133
|
+
const result = await this.builder.getNativeTransaction(
|
|
134
|
+
tx as EVMNativeTransferRequest
|
|
135
|
+
)
|
|
136
|
+
return {
|
|
137
|
+
to: result.to ?? '',
|
|
138
|
+
value: '0x' + (result.value ?? 0n).toString(16)
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
throw new Error('Invalid transaction request type')
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
// Create ethers provider
|
|
147
|
+
const ethersProvider = new ethers.BrowserProvider(provider)
|
|
148
|
+
|
|
149
|
+
// Send batch transaction
|
|
150
|
+
const response: { id: string } = await ethersProvider.send(
|
|
151
|
+
'wallet_sendCalls',
|
|
152
|
+
[
|
|
153
|
+
{
|
|
154
|
+
version: request.version,
|
|
155
|
+
from: request.from,
|
|
156
|
+
chainId: request.chainId,
|
|
157
|
+
atomicRequired: request.atomicRequired,
|
|
158
|
+
calls: calls
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
// Poll for completion
|
|
164
|
+
let result: BatchTransactionResult
|
|
165
|
+
do {
|
|
166
|
+
result = await ethersProvider.send('wallet_getCallsStatus', [
|
|
167
|
+
response.id
|
|
168
|
+
])
|
|
169
|
+
|
|
170
|
+
// Wait 1 second if still pending
|
|
171
|
+
if (result.status === 100) {
|
|
172
|
+
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
173
|
+
}
|
|
174
|
+
} while (result.status === 100)
|
|
175
|
+
|
|
176
|
+
// Check if successful
|
|
177
|
+
if (result.status !== 200) {
|
|
178
|
+
throw new Error('Batch transaction failed')
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Return first transaction hash
|
|
182
|
+
const firstReceipt = result.receipts.find(r => r)
|
|
183
|
+
if (!firstReceipt) {
|
|
184
|
+
throw new Error('No transaction receipt found')
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return firstReceipt.transactionHash
|
|
188
|
+
} catch (error) {
|
|
189
|
+
parseError(error)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
NetworkId,
|
|
3
|
+
AvailableAddress,
|
|
4
|
+
EVMCapabilities,
|
|
5
|
+
EVMCapabilityStatus
|
|
6
|
+
} from '@meshconnect/uwc-types'
|
|
2
7
|
import type {
|
|
3
8
|
EthereumProvider,
|
|
4
9
|
DetectedWallet as EIP6963DetectedWallet
|
|
5
|
-
} from '
|
|
6
|
-
import { getAvailableWallets as getEIP6963Wallets } from '
|
|
10
|
+
} from '../../eip6963-discovery'
|
|
11
|
+
import { getAvailableWallets as getEIP6963Wallets } from '../../eip6963-discovery'
|
|
7
12
|
|
|
8
13
|
/**
|
|
9
14
|
* Service for managing Ethereum wallet connections
|
|
@@ -158,4 +163,52 @@ export class EthereumWalletService {
|
|
|
158
163
|
this.connectedProvider = null
|
|
159
164
|
this.account = null
|
|
160
165
|
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get wallet capabilities (EIP-5792 support)
|
|
169
|
+
*/
|
|
170
|
+
async getCapabilities(
|
|
171
|
+
from: string
|
|
172
|
+
): Promise<Record<string, EVMCapabilities>> {
|
|
173
|
+
if (!this.connectedProvider) {
|
|
174
|
+
throw new Error('No Ethereum wallet connected')
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
178
|
+
let raw: Record<`0x${string}`, Record<string, any>>
|
|
179
|
+
try {
|
|
180
|
+
raw = await this.connectedProvider.request({
|
|
181
|
+
method: 'wallet_getCapabilities',
|
|
182
|
+
params: [from]
|
|
183
|
+
})
|
|
184
|
+
} catch {
|
|
185
|
+
return {}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const result: Record<string, EVMCapabilities> = {}
|
|
189
|
+
|
|
190
|
+
for (const [chainId, caps] of Object.entries(raw || {})) {
|
|
191
|
+
const networkId = `eip155:${Number(chainId)}`
|
|
192
|
+
result[networkId] = {}
|
|
193
|
+
|
|
194
|
+
for (const [capKey, capValue] of Object.entries(caps)) {
|
|
195
|
+
if (capValue && typeof capValue === 'object') {
|
|
196
|
+
if (typeof capValue.supported === 'boolean') {
|
|
197
|
+
result[networkId][capKey as keyof EVMCapabilities] = {
|
|
198
|
+
status: capValue.supported ? 'supported' : 'unsupported'
|
|
199
|
+
}
|
|
200
|
+
} else if (
|
|
201
|
+
typeof capValue.status === 'string' &&
|
|
202
|
+
['supported', 'ready', 'unsupported'].includes(capValue.status)
|
|
203
|
+
) {
|
|
204
|
+
result[networkId][capKey as keyof EVMCapabilities] = {
|
|
205
|
+
status: capValue.status as EVMCapabilityStatus
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return result
|
|
213
|
+
}
|
|
161
214
|
}
|
package/src/services/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { StorageService } from './storage-service'
|
|
2
|
-
export { EthereumWalletService } from './ethereum-wallet-service'
|
|
3
|
-
export { SolanaWalletService } from './solana-wallet-service'
|
|
2
|
+
export { EthereumWalletService } from './ethereum/ethereum-wallet-service'
|
|
3
|
+
export { SolanaWalletService } from './solana/solana-wallet-service'
|
|
4
4
|
export { ConnectionManager } from './connection-manager'
|
|
5
5
|
export { SignatureService } from './signature-service'
|
|
6
6
|
export { TransactionService } from './transaction-service'
|
|
7
|
-
export { EthereumTransactionService } from './ethereum-transaction-service'
|
|
7
|
+
export { EthereumTransactionService } from './ethereum/ethereum-transaction-service'
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AddressLookupTableAccount,
|
|
3
|
+
PublicKey,
|
|
4
|
+
SystemProgram,
|
|
5
|
+
TransactionInstruction,
|
|
6
|
+
TransactionMessage,
|
|
7
|
+
VersionedTransaction,
|
|
8
|
+
type AddressLookupTableState
|
|
9
|
+
} from '@solana/web3.js'
|
|
10
|
+
import type {
|
|
11
|
+
SolanaNativeTransferRequest,
|
|
12
|
+
SolanaTokenTransferRequest,
|
|
13
|
+
SolanaGenericTransferRequest,
|
|
14
|
+
SolanaAccountMeta,
|
|
15
|
+
SolanaTransactionRequest
|
|
16
|
+
} from '@meshconnect/uwc-types'
|
|
17
|
+
import {
|
|
18
|
+
createTransferCheckedInstruction,
|
|
19
|
+
createAssociatedTokenAccountIdempotentInstruction,
|
|
20
|
+
getAssociatedTokenAddress,
|
|
21
|
+
TOKEN_PROGRAM_ID,
|
|
22
|
+
TOKEN_2022_PROGRAM_ID
|
|
23
|
+
} from '@solana/spl-token'
|
|
24
|
+
import { Buffer } from 'buffer'
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Service for handling Solana transaction operations
|
|
28
|
+
*/
|
|
29
|
+
export class SolanaTransactionBuilder {
|
|
30
|
+
async buildTransferInstructions(
|
|
31
|
+
request: SolanaTransactionRequest
|
|
32
|
+
): Promise<TransactionInstruction[]> {
|
|
33
|
+
if ('tokenMint' in request) {
|
|
34
|
+
return await this.buildSplTokenTransferInstructions(
|
|
35
|
+
request as SolanaTokenTransferRequest
|
|
36
|
+
)
|
|
37
|
+
} else if ('instructions' in request) {
|
|
38
|
+
return await this.buildGenericTransferInstructions(
|
|
39
|
+
request as SolanaGenericTransferRequest
|
|
40
|
+
)
|
|
41
|
+
} else {
|
|
42
|
+
return [
|
|
43
|
+
this.buildNativeTransferInstruction(
|
|
44
|
+
request as SolanaNativeTransferRequest
|
|
45
|
+
)
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async getVersionedTransaction(
|
|
51
|
+
feePayer: PublicKey,
|
|
52
|
+
request: SolanaTransactionRequest
|
|
53
|
+
) {
|
|
54
|
+
const lookupTable = this.parseLookupTable(request)
|
|
55
|
+
|
|
56
|
+
return new VersionedTransaction(
|
|
57
|
+
new TransactionMessage({
|
|
58
|
+
payerKey: feePayer,
|
|
59
|
+
recentBlockhash: request.blockhash,
|
|
60
|
+
instructions: await this.buildTransferInstructions(request)
|
|
61
|
+
}).compileToV0Message(lookupTable)
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Build native SOL transfer
|
|
67
|
+
*/
|
|
68
|
+
private buildNativeTransferInstruction(
|
|
69
|
+
request: SolanaNativeTransferRequest
|
|
70
|
+
): TransactionInstruction {
|
|
71
|
+
return SystemProgram.transfer({
|
|
72
|
+
fromPubkey: new PublicKey(request.from),
|
|
73
|
+
toPubkey: new PublicKey(request.to),
|
|
74
|
+
lamports: Number(request.amount)
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Build SPL Token transfer
|
|
80
|
+
*/
|
|
81
|
+
private async buildSplTokenTransferInstructions(
|
|
82
|
+
request: SolanaTokenTransferRequest
|
|
83
|
+
): Promise<TransactionInstruction[]> {
|
|
84
|
+
let instructions: TransactionInstruction[] = []
|
|
85
|
+
|
|
86
|
+
// Create Public Keys
|
|
87
|
+
const mint = new PublicKey(request.tokenMint)
|
|
88
|
+
const from = new PublicKey(request.from)
|
|
89
|
+
const to = new PublicKey(request.to)
|
|
90
|
+
|
|
91
|
+
const fromTokenAccount = await getAssociatedTokenAddress(
|
|
92
|
+
mint,
|
|
93
|
+
from,
|
|
94
|
+
false,
|
|
95
|
+
request.tokenProgram
|
|
96
|
+
? new PublicKey(request.tokenProgram)
|
|
97
|
+
: TOKEN_PROGRAM_ID
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
const toTokenAccount = await getAssociatedTokenAddress(
|
|
101
|
+
mint,
|
|
102
|
+
to,
|
|
103
|
+
false,
|
|
104
|
+
request.tokenProgram
|
|
105
|
+
? new PublicKey(request.tokenProgram)
|
|
106
|
+
: TOKEN_PROGRAM_ID
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
// Create target associated token account
|
|
110
|
+
instructions.push(
|
|
111
|
+
createAssociatedTokenAccountIdempotentInstruction(
|
|
112
|
+
from,
|
|
113
|
+
toTokenAccount,
|
|
114
|
+
to,
|
|
115
|
+
mint,
|
|
116
|
+
request.tokenProgram
|
|
117
|
+
? new PublicKey(request.tokenProgram)
|
|
118
|
+
: TOKEN_PROGRAM_ID
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
const programId =
|
|
123
|
+
request.tokenProgram === TOKEN_2022_PROGRAM_ID.toBase58()
|
|
124
|
+
? TOKEN_2022_PROGRAM_ID
|
|
125
|
+
: TOKEN_PROGRAM_ID
|
|
126
|
+
|
|
127
|
+
instructions.push(
|
|
128
|
+
createTransferCheckedInstruction(
|
|
129
|
+
fromTokenAccount,
|
|
130
|
+
mint,
|
|
131
|
+
toTokenAccount,
|
|
132
|
+
from,
|
|
133
|
+
request.amount,
|
|
134
|
+
request.tokenDecimals,
|
|
135
|
+
[],
|
|
136
|
+
programId
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
return instructions
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private async buildGenericTransferInstructions(
|
|
144
|
+
request: SolanaGenericTransferRequest
|
|
145
|
+
): Promise<TransactionInstruction[]> {
|
|
146
|
+
const result: TransactionInstruction[] = []
|
|
147
|
+
|
|
148
|
+
const instructions = request.instructions
|
|
149
|
+
for (let instrIndex = 0; instrIndex < instructions.length; instrIndex++) {
|
|
150
|
+
const ix = instructions[instrIndex]!
|
|
151
|
+
const programId = new PublicKey(ix.programId)
|
|
152
|
+
|
|
153
|
+
const keys = ix.accounts.map(
|
|
154
|
+
(meta: SolanaAccountMeta, accountIndex: number) => {
|
|
155
|
+
if (!meta.pubKey) {
|
|
156
|
+
throw new Error(
|
|
157
|
+
`Account at instruction ${instrIndex}, index ${accountIndex} has no pubKey and is not fillable`
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const resolvedPubkey: PublicKey = new PublicKey(meta.pubKey)
|
|
162
|
+
return {
|
|
163
|
+
pubkey: resolvedPubkey,
|
|
164
|
+
isSigner: meta.isSigner,
|
|
165
|
+
isWritable: meta.isWritable
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
result.push(
|
|
171
|
+
new TransactionInstruction({
|
|
172
|
+
keys,
|
|
173
|
+
programId,
|
|
174
|
+
data: Buffer.from(ix.data, 'base64')
|
|
175
|
+
})
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (request.additionalRequests) {
|
|
180
|
+
const extraInstructions = await Promise.all(
|
|
181
|
+
request.additionalRequests.map(req =>
|
|
182
|
+
this.buildTransferInstructions(req)
|
|
183
|
+
)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
result.push(...extraInstructions.flat())
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return result
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private parseLookupTable = (
|
|
193
|
+
request: SolanaTransactionRequest
|
|
194
|
+
): AddressLookupTableAccount[] => {
|
|
195
|
+
if (!('states' in request)) {
|
|
196
|
+
return []
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return (request.states ?? []).map(state => {
|
|
200
|
+
const currentState: AddressLookupTableState = {
|
|
201
|
+
deactivationSlot: state.deactivationSlot,
|
|
202
|
+
lastExtendedSlot: state.lastExtendedSlot,
|
|
203
|
+
lastExtendedSlotStartIndex: state.lastExtendedStartIndex,
|
|
204
|
+
addresses: [],
|
|
205
|
+
...(state.authority && { authority: new PublicKey(state.authority) })
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return new AddressLookupTableAccount({
|
|
209
|
+
key: new PublicKey(state.key),
|
|
210
|
+
state: currentState
|
|
211
|
+
})
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { Transaction, Connection } from '@solana/web3.js'
|
|
2
|
+
import type { WalletAdapter } from '@solana/wallet-adapter-base'
|
|
3
|
+
import type {
|
|
4
|
+
SolanaNativeTransferRequest,
|
|
5
|
+
NetworkRpcMap,
|
|
6
|
+
NetworkId,
|
|
7
|
+
SolanaTokenTransferRequest,
|
|
8
|
+
SolanaGenericTransferRequest,
|
|
9
|
+
SolanaTransactionRequest
|
|
10
|
+
} from '@meshconnect/uwc-types'
|
|
11
|
+
import { parseError } from '../../utils/error-utils'
|
|
12
|
+
import { SolanaTransactionBuilder } from './solana-transaction-builder'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Service for handling Solana transaction operations
|
|
16
|
+
*/
|
|
17
|
+
export class SolanaTransactionService {
|
|
18
|
+
private networkRpcMap: NetworkRpcMap
|
|
19
|
+
private builder: SolanaTransactionBuilder
|
|
20
|
+
|
|
21
|
+
constructor(networkRpcMap: NetworkRpcMap = {}) {
|
|
22
|
+
this.networkRpcMap = networkRpcMap
|
|
23
|
+
this.builder = new SolanaTransactionBuilder()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Send a transaction for Solana
|
|
28
|
+
*/
|
|
29
|
+
async sendTransaction(
|
|
30
|
+
request: SolanaTransactionRequest,
|
|
31
|
+
adapter: WalletAdapter
|
|
32
|
+
): Promise<string> {
|
|
33
|
+
try {
|
|
34
|
+
// Handle different types of Solana transactions
|
|
35
|
+
if ('tokenMint' in request) {
|
|
36
|
+
return await this.sendSplTokenTransaction(
|
|
37
|
+
adapter,
|
|
38
|
+
request as SolanaTokenTransferRequest
|
|
39
|
+
)
|
|
40
|
+
} else if ('from' in request) {
|
|
41
|
+
return await this.sendNativeTransaction(
|
|
42
|
+
adapter,
|
|
43
|
+
request as SolanaNativeTransferRequest
|
|
44
|
+
)
|
|
45
|
+
} else if ('instructions' in request) {
|
|
46
|
+
return await this.sendGenericTransferWithInstructions(
|
|
47
|
+
adapter,
|
|
48
|
+
request as SolanaGenericTransferRequest
|
|
49
|
+
)
|
|
50
|
+
} else {
|
|
51
|
+
throw new Error('Invalid transaction request type')
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
parseError(error)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Send native SOL transfer
|
|
60
|
+
*/
|
|
61
|
+
private async sendNativeTransaction(
|
|
62
|
+
adapter: WalletAdapter,
|
|
63
|
+
request: SolanaNativeTransferRequest,
|
|
64
|
+
networkId?: NetworkId
|
|
65
|
+
): Promise<string> {
|
|
66
|
+
try {
|
|
67
|
+
if (!adapter.publicKey) {
|
|
68
|
+
throw new Error('Wallet not connected')
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Verify the from address matches connected wallet
|
|
72
|
+
if (adapter.publicKey.toBase58() !== request.from) {
|
|
73
|
+
throw new Error('From address does not match connected wallet')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Blockhash is required
|
|
77
|
+
if (!request.blockhash) {
|
|
78
|
+
throw new Error('Blockhash is required for Solana transactions')
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Create transaction
|
|
82
|
+
const transaction = new Transaction()
|
|
83
|
+
transaction.instructions =
|
|
84
|
+
await this.builder.buildTransferInstructions(request)
|
|
85
|
+
|
|
86
|
+
transaction.recentBlockhash = request.blockhash
|
|
87
|
+
transaction.feePayer = adapter.publicKey
|
|
88
|
+
|
|
89
|
+
// Get RPC URL from the network RPC map or fall back to public endpoint
|
|
90
|
+
const rpcUrl =
|
|
91
|
+
networkId && this.networkRpcMap[networkId]
|
|
92
|
+
? this.networkRpcMap[networkId]
|
|
93
|
+
: 'https://api.mainnet-beta.solana.com'
|
|
94
|
+
const connection = new Connection(rpcUrl)
|
|
95
|
+
|
|
96
|
+
const signature = await adapter.sendTransaction(transaction, connection)
|
|
97
|
+
return signature
|
|
98
|
+
} catch (error) {
|
|
99
|
+
parseError(error)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Send SPL token transfer
|
|
105
|
+
*/
|
|
106
|
+
private async sendSplTokenTransaction(
|
|
107
|
+
adapter: WalletAdapter,
|
|
108
|
+
request: SolanaTokenTransferRequest,
|
|
109
|
+
networkId?: NetworkId
|
|
110
|
+
): Promise<string> {
|
|
111
|
+
try {
|
|
112
|
+
if (!adapter.publicKey) {
|
|
113
|
+
throw new Error('Wallet not connected')
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Verify the from address matches connected wallet
|
|
117
|
+
if (adapter.publicKey.toBase58() !== request.from) {
|
|
118
|
+
throw new Error('From address does not match connected wallet')
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Blockhash is required
|
|
122
|
+
if (!request.blockhash) {
|
|
123
|
+
throw new Error('Blockhash is required for Solana transactions')
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Create transaction
|
|
127
|
+
const transaction = new Transaction()
|
|
128
|
+
transaction.instructions =
|
|
129
|
+
await this.builder.buildTransferInstructions(request)
|
|
130
|
+
|
|
131
|
+
transaction.recentBlockhash = request.blockhash
|
|
132
|
+
transaction.feePayer = adapter.publicKey
|
|
133
|
+
|
|
134
|
+
// Get RPC URL from the network RPC map or fall back to public endpoint
|
|
135
|
+
const rpcUrl =
|
|
136
|
+
networkId && this.networkRpcMap[networkId]
|
|
137
|
+
? this.networkRpcMap[networkId]
|
|
138
|
+
: 'https://api.mainnet-beta.solana.com'
|
|
139
|
+
const connection = new Connection(rpcUrl)
|
|
140
|
+
|
|
141
|
+
const signature = await adapter.sendTransaction(transaction, connection)
|
|
142
|
+
return signature
|
|
143
|
+
} catch (error) {
|
|
144
|
+
parseError(error)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Send native SOL transfer
|
|
150
|
+
*/
|
|
151
|
+
private async sendGenericTransferWithInstructions(
|
|
152
|
+
adapter: WalletAdapter,
|
|
153
|
+
request: SolanaGenericTransferRequest,
|
|
154
|
+
networkId?: NetworkId
|
|
155
|
+
): Promise<string> {
|
|
156
|
+
try {
|
|
157
|
+
if (!adapter.publicKey) {
|
|
158
|
+
throw new Error('Wallet not connected')
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Verify the from address matches connected wallet
|
|
162
|
+
if (adapter.publicKey.toBase58() !== request.feePayer) {
|
|
163
|
+
throw new Error('From address does not match connected wallet')
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Blockhash is required
|
|
167
|
+
if (!request.blockhash) {
|
|
168
|
+
throw new Error('Blockhash is required for Solana transactions')
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Create transaction
|
|
172
|
+
const transaction = await this.builder.getVersionedTransaction(
|
|
173
|
+
adapter.publicKey,
|
|
174
|
+
request
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
// Get RPC URL from the network RPC map or fall back to public endpoint
|
|
178
|
+
const rpcUrl =
|
|
179
|
+
networkId && this.networkRpcMap[networkId]
|
|
180
|
+
? this.networkRpcMap[networkId]
|
|
181
|
+
: 'https://api.mainnet-beta.solana.com'
|
|
182
|
+
const connection = new Connection(rpcUrl)
|
|
183
|
+
|
|
184
|
+
const signature = await adapter.sendTransaction(transaction, connection)
|
|
185
|
+
return signature
|
|
186
|
+
} catch (error) {
|
|
187
|
+
parseError(error)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -3,8 +3,8 @@ import type { WalletAdapter } from '@solana/wallet-adapter-base'
|
|
|
3
3
|
import {
|
|
4
4
|
getSolanaWallets,
|
|
5
5
|
type WalletStandardInfo
|
|
6
|
-
} from '
|
|
7
|
-
import { StorageService } from '
|
|
6
|
+
} from '../../wallet-standard-discovery'
|
|
7
|
+
import { StorageService } from '../storage-service'
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Service for managing Solana wallet connections
|