@meshconnect/uwc-wallet-connect-connector 0.2.1 → 0.2.2
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/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 +21 -0
- package/dist/services/ethereum/ethereum-transaction-service.d.ts.map +1 -0
- package/dist/services/ethereum/ethereum-transaction-service.js +107 -0
- package/dist/services/ethereum/ethereum-transaction-service.js.map +1 -0
- 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 +17 -0
- package/dist/services/solana/solana-transaction-service.d.ts.map +1 -0
- package/dist/services/solana/solana-transaction-service.js +178 -0
- package/dist/services/solana/solana-transaction-service.js.map +1 -0
- package/dist/services/transaction-service.d.ts +2 -17
- package/dist/services/transaction-service.d.ts.map +1 -1
- package/dist/services/transaction-service.js +8 -246
- package/dist/services/transaction-service.js.map +1 -1
- package/package.json +3 -2
- package/src/services/ethereum/ethereum-transaction-builder.ts +82 -0
- package/src/services/ethereum/ethereum-transaction-service.ts +152 -0
- package/src/services/solana/solana-transaction-builder.ts +214 -0
- package/src/services/solana/solana-transaction-service.ts +294 -0
- package/src/services/transaction-service.ts +17 -339
- package/src/utils/account-utils.ts +1 -1
- package/src/utils/solana-transactions.ts +0 -24
|
@@ -1,24 +1,20 @@
|
|
|
1
|
-
import { ethers } from 'ethers';
|
|
2
|
-
import { Buffer } from 'buffer';
|
|
3
|
-
import { Connection, PublicKey, Transaction, VersionedTransaction } from '@solana/web3.js';
|
|
4
1
|
import { WalletConnectClient } from './walletconnect-client';
|
|
5
2
|
import { ConnectionService } from './connection-service';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { getSolanaTransaction } from '../utils/solana-transactions';
|
|
9
|
-
import base58 from 'bs58';
|
|
10
|
-
import { standardizeSignature } from '../utils/encoding-utils';
|
|
3
|
+
import { EthereumTransactionService } from './ethereum/ethereum-transaction-service';
|
|
4
|
+
import { SolanaTransactionService } from './solana/solana-transaction-service';
|
|
11
5
|
/**
|
|
12
6
|
* Service for handling transactions in WalletConnect
|
|
13
7
|
*/
|
|
14
8
|
export class TransactionService {
|
|
15
9
|
walletConnectClient;
|
|
16
10
|
connectionService;
|
|
17
|
-
|
|
11
|
+
ethereumService;
|
|
12
|
+
solanaService;
|
|
18
13
|
constructor(walletConnectClient, connectionService, networkRpcMap = {}) {
|
|
19
14
|
this.walletConnectClient = walletConnectClient;
|
|
20
15
|
this.connectionService = connectionService;
|
|
21
|
-
this.
|
|
16
|
+
this.ethereumService = new EthereumTransactionService();
|
|
17
|
+
this.solanaService = new SolanaTransactionService(networkRpcMap);
|
|
22
18
|
}
|
|
23
19
|
/**
|
|
24
20
|
* Send a transaction with the connected wallet
|
|
@@ -40,248 +36,14 @@ export class TransactionService {
|
|
|
40
36
|
// Determine namespace from network ID
|
|
41
37
|
const namespace = activeNetwork.id.split(':')[0];
|
|
42
38
|
if (namespace === 'eip155') {
|
|
43
|
-
return this.
|
|
39
|
+
return this.ethereumService.sendTransaction(client, session, request, activeNetwork.id);
|
|
44
40
|
}
|
|
45
41
|
else if (namespace === 'solana') {
|
|
46
|
-
return this.
|
|
42
|
+
return this.solanaService.sendTransaction(client, session, request, activeNetwork.id, activeAddress);
|
|
47
43
|
}
|
|
48
44
|
else {
|
|
49
45
|
throw new Error(`Unsupported namespace: ${namespace}`);
|
|
50
46
|
}
|
|
51
47
|
}
|
|
52
|
-
/**
|
|
53
|
-
* Send an Ethereum transaction
|
|
54
|
-
*/
|
|
55
|
-
async sendEthereumTransaction(client, session, request, activeNetworkId, activeAddress) {
|
|
56
|
-
try {
|
|
57
|
-
// Handle different types of EVM transactions
|
|
58
|
-
if ('contractAddress' in request && 'abi' in request) {
|
|
59
|
-
// Contract call
|
|
60
|
-
return await this.sendContractCall(client, session, request, activeNetworkId, activeAddress);
|
|
61
|
-
}
|
|
62
|
-
else if ('calls' in request) {
|
|
63
|
-
// Batch transaction - not yet supported
|
|
64
|
-
throw new Error('Batch transactions are not yet supported for WalletConnect');
|
|
65
|
-
}
|
|
66
|
-
else if ('amount' in request && typeof request.amount === 'bigint') {
|
|
67
|
-
// Native transfer
|
|
68
|
-
return await this.sendNativeTransfer(client, session, request, activeNetworkId, activeAddress);
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
throw new Error('Invalid transaction request type');
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
throw parseError(error);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Send a Solana transaction
|
|
80
|
-
*/
|
|
81
|
-
async sendSolanaTransaction(client, session, request, activeNetworkId, activeAddress) {
|
|
82
|
-
try {
|
|
83
|
-
// Verify the from address matches active address
|
|
84
|
-
if (request.from !== activeAddress) {
|
|
85
|
-
throw new Error('From address does not match connected wallet');
|
|
86
|
-
}
|
|
87
|
-
// Blockhash is required
|
|
88
|
-
if (!request.blockhash) {
|
|
89
|
-
throw new Error('Blockhash is required for Solana transactions');
|
|
90
|
-
}
|
|
91
|
-
// Create the transaction using the utility function
|
|
92
|
-
let transaction = getSolanaTransaction(request);
|
|
93
|
-
// Serialize the transaction for WalletConnect
|
|
94
|
-
const serializedTransaction = transaction
|
|
95
|
-
.serialize({
|
|
96
|
-
verifySignatures: false
|
|
97
|
-
})
|
|
98
|
-
.toString('base64');
|
|
99
|
-
// Get the chain ID (e.g., "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" for mainnet)
|
|
100
|
-
const chainId = activeNetworkId;
|
|
101
|
-
try {
|
|
102
|
-
// First try signAndSendTransaction (preferred method)
|
|
103
|
-
const result = await client.request({
|
|
104
|
-
topic: session.topic,
|
|
105
|
-
chainId,
|
|
106
|
-
request: {
|
|
107
|
-
method: 'solana_signAndSendTransaction',
|
|
108
|
-
params: {
|
|
109
|
-
transaction: serializedTransaction
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
// WalletConnect returns the signature directly for Solana
|
|
114
|
-
return result;
|
|
115
|
-
}
|
|
116
|
-
catch (signAndSendError) {
|
|
117
|
-
const errorMessage = signAndSendError instanceof Error
|
|
118
|
-
? signAndSendError.message
|
|
119
|
-
: String(signAndSendError);
|
|
120
|
-
// If signAndSendTransaction is not supported, fallback to signTransaction
|
|
121
|
-
if (errorMessage.includes('solana_signAndSendTransaction') ||
|
|
122
|
-
errorMessage.includes('Missing or invalid')) {
|
|
123
|
-
// Sign the transaction only
|
|
124
|
-
const result = await client.request({
|
|
125
|
-
topic: session.topic,
|
|
126
|
-
chainId,
|
|
127
|
-
request: {
|
|
128
|
-
method: 'solana_signTransaction',
|
|
129
|
-
params: {
|
|
130
|
-
transaction: serializedTransaction
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
if ('signature' in result) {
|
|
135
|
-
const decoded = base58.decode(result.signature);
|
|
136
|
-
transaction.addSignature(new PublicKey(request.from), Buffer.from(decoded));
|
|
137
|
-
}
|
|
138
|
-
if ('transaction' in result) {
|
|
139
|
-
const decodedTransaction = Buffer.from(result.transaction, 'base64');
|
|
140
|
-
if (transaction instanceof VersionedTransaction) {
|
|
141
|
-
transaction = VersionedTransaction.deserialize(new Uint8Array(decodedTransaction));
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
transaction = Transaction.from(decodedTransaction);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
// Get RPC URL from the network RPC map or fall back to public endpoint
|
|
148
|
-
const rpcUrl = this.networkRpcMap[activeNetworkId] ||
|
|
149
|
-
'https://api.mainnet-beta.solana.com';
|
|
150
|
-
const connection = new Connection(rpcUrl);
|
|
151
|
-
const rawSignature = await connection.sendRawTransaction(transaction.serialize(), {
|
|
152
|
-
skipPreflight: false,
|
|
153
|
-
maxRetries: 3,
|
|
154
|
-
preflightCommitment: 'confirmed'
|
|
155
|
-
});
|
|
156
|
-
return standardizeSignature(rawSignature);
|
|
157
|
-
}
|
|
158
|
-
// If it's a different error, throw it
|
|
159
|
-
throw signAndSendError;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
catch (error) {
|
|
163
|
-
throw parseError(error);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Send native token transfer (ETH, BNB, MATIC, etc.)
|
|
168
|
-
*/
|
|
169
|
-
async sendNativeTransfer(client, session, request, activeNetworkId, activeAddress) {
|
|
170
|
-
try {
|
|
171
|
-
// Use the active network chainId
|
|
172
|
-
const chainId = getChainIdFromNetworkId(activeNetworkId);
|
|
173
|
-
// Build transaction using ethers
|
|
174
|
-
const tx = {
|
|
175
|
-
from: request.from || activeAddress,
|
|
176
|
-
to: request.to,
|
|
177
|
-
value: request.amount
|
|
178
|
-
};
|
|
179
|
-
// Add gas configuration if provided
|
|
180
|
-
if (request.gasConfig?.gasLimit) {
|
|
181
|
-
tx.gasLimit = BigInt(Math.floor(request.gasConfig.gasLimit));
|
|
182
|
-
}
|
|
183
|
-
if (request.gasConfig?.maxFeePerGas) {
|
|
184
|
-
tx.maxFeePerGas = BigInt(Math.floor(request.gasConfig.maxFeePerGas));
|
|
185
|
-
}
|
|
186
|
-
if (request.gasConfig?.maxPriorityFeePerGas) {
|
|
187
|
-
tx.maxPriorityFeePerGas = BigInt(Math.floor(request.gasConfig.maxPriorityFeePerGas));
|
|
188
|
-
}
|
|
189
|
-
// Convert to hex format for WalletConnect
|
|
190
|
-
const txParams = {
|
|
191
|
-
from: tx.from,
|
|
192
|
-
to: tx.to,
|
|
193
|
-
value: '0x' + (tx.value ? BigInt(tx.value).toString(16) : '0'),
|
|
194
|
-
data: '0x' // Empty data for native transfers
|
|
195
|
-
};
|
|
196
|
-
// Add gas params if present
|
|
197
|
-
if (tx.gasLimit) {
|
|
198
|
-
txParams['gasLimit'] = '0x' + tx.gasLimit.toString(16);
|
|
199
|
-
}
|
|
200
|
-
if (tx.maxFeePerGas) {
|
|
201
|
-
txParams['maxFeePerGas'] = '0x' + tx.maxFeePerGas.toString(16);
|
|
202
|
-
}
|
|
203
|
-
if (tx.maxPriorityFeePerGas) {
|
|
204
|
-
txParams['maxPriorityFeePerGas'] =
|
|
205
|
-
'0x' + tx.maxPriorityFeePerGas.toString(16);
|
|
206
|
-
}
|
|
207
|
-
const hash = await client.request({
|
|
208
|
-
topic: session.topic,
|
|
209
|
-
chainId: `eip155:${chainId}`,
|
|
210
|
-
request: {
|
|
211
|
-
method: 'eth_sendTransaction',
|
|
212
|
-
params: [txParams]
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
return hash;
|
|
216
|
-
}
|
|
217
|
-
catch (error) {
|
|
218
|
-
throw parseError(error);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Send smart contract transaction (including ERC-20 transfers)
|
|
223
|
-
*/
|
|
224
|
-
async sendContractCall(client, session, request, activeNetworkId, activeAddress) {
|
|
225
|
-
try {
|
|
226
|
-
// Use the active network chainId
|
|
227
|
-
const chainId = getChainIdFromNetworkId(activeNetworkId);
|
|
228
|
-
// Encode the contract call data using ethers
|
|
229
|
-
const iface = new ethers.Interface(request.abi);
|
|
230
|
-
const data = iface.encodeFunctionData(request.functionName, request.args);
|
|
231
|
-
// Build transaction using ethers
|
|
232
|
-
const tx = {
|
|
233
|
-
from: request.from || activeAddress,
|
|
234
|
-
to: request.contractAddress,
|
|
235
|
-
data
|
|
236
|
-
};
|
|
237
|
-
// Add value for payable functions
|
|
238
|
-
if (request.value) {
|
|
239
|
-
tx.value = BigInt(request.value);
|
|
240
|
-
}
|
|
241
|
-
// Add gas configuration if provided
|
|
242
|
-
if (request.gasConfig?.gasLimit) {
|
|
243
|
-
tx.gasLimit = BigInt(Math.floor(request.gasConfig.gasLimit));
|
|
244
|
-
}
|
|
245
|
-
if (request.gasConfig?.maxFeePerGas) {
|
|
246
|
-
tx.maxFeePerGas = BigInt(Math.floor(request.gasConfig.maxFeePerGas));
|
|
247
|
-
}
|
|
248
|
-
if (request.gasConfig?.maxPriorityFeePerGas) {
|
|
249
|
-
tx.maxPriorityFeePerGas = BigInt(Math.floor(request.gasConfig.maxPriorityFeePerGas));
|
|
250
|
-
}
|
|
251
|
-
// Convert to hex format for WalletConnect
|
|
252
|
-
const txParams = {
|
|
253
|
-
from: tx.from,
|
|
254
|
-
to: tx.to,
|
|
255
|
-
data: tx.data
|
|
256
|
-
};
|
|
257
|
-
// Add value if present
|
|
258
|
-
if (tx.value) {
|
|
259
|
-
txParams['value'] = '0x' + BigInt(tx.value).toString(16);
|
|
260
|
-
}
|
|
261
|
-
// Add gas params if present
|
|
262
|
-
if (tx.gasLimit) {
|
|
263
|
-
txParams['gasLimit'] = '0x' + tx.gasLimit.toString(16);
|
|
264
|
-
}
|
|
265
|
-
if (tx.maxFeePerGas) {
|
|
266
|
-
txParams['maxFeePerGas'] = '0x' + tx.maxFeePerGas.toString(16);
|
|
267
|
-
}
|
|
268
|
-
if (tx.maxPriorityFeePerGas) {
|
|
269
|
-
txParams['maxPriorityFeePerGas'] =
|
|
270
|
-
'0x' + tx.maxPriorityFeePerGas.toString(16);
|
|
271
|
-
}
|
|
272
|
-
const hash = await client.request({
|
|
273
|
-
topic: session.topic,
|
|
274
|
-
chainId: `eip155:${chainId}`,
|
|
275
|
-
request: {
|
|
276
|
-
method: 'eth_sendTransaction',
|
|
277
|
-
params: [txParams]
|
|
278
|
-
}
|
|
279
|
-
});
|
|
280
|
-
return hash;
|
|
281
|
-
}
|
|
282
|
-
catch (error) {
|
|
283
|
-
throw parseError(error);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
48
|
}
|
|
287
49
|
//# sourceMappingURL=transaction-service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transaction-service.js","sourceRoot":"","sources":["../../src/services/transaction-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"transaction-service.js","sourceRoot":"","sources":["../../src/services/transaction-service.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AACxD,OAAO,EAAE,0BAA0B,EAAE,MAAM,yCAAyC,CAAA;AACpF,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAA;AAE9E;;GAEG;AACH,MAAM,OAAO,kBAAkB;IAKnB;IACA;IALF,eAAe,CAA4B;IAC3C,aAAa,CAA0B;IAE/C,YACU,mBAAwC,EACxC,iBAAoC,EAC5C,gBAA+B,EAAE;QAFzB,wBAAmB,GAAnB,mBAAmB,CAAqB;QACxC,sBAAiB,GAAjB,iBAAiB,CAAmB;QAG5C,IAAI,CAAC,eAAe,GAAG,IAAI,0BAA0B,EAAE,CAAA;QACvD,IAAI,CAAC,aAAa,GAAG,IAAI,wBAAwB,CAAC,aAAa,CAAC,CAAA;IAClE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CACnB,OAA2B;QAE3B,MAAM,OAAO,GAAG,IAAI,CAAC,iBAAiB,CAAC,UAAU,EAAE,CAAA;QACnD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;QAC7D,CAAC;QAED,MAAM,aAAa,GAAG,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,EAAE,CAAA;QAC/D,MAAM,aAAa,GAAG,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,EAAE,CAAA;QAE/D,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;QAC7D,CAAC;QAED,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;QAC7D,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAAE,CAAA;QAEzD,sCAAsC;QACtC,MAAM,SAAS,GAAG,aAAa,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;QAEhD,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC,eAAe,CAAC,eAAe,CACzC,MAAM,EACN,OAAO,EACP,OAAqC,EACrC,aAAa,CAAC,EAAE,CACjB,CAAA;QACH,CAAC;aAAM,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC,aAAa,CAAC,eAAe,CACvC,MAAM,EACN,OAAO,EACP,OAAmC,EACnC,aAAa,CAAC,EAAE,EAChB,aAAa,CACd,CAAA;QACH,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,0BAA0B,SAAS,EAAE,CAAC,CAAA;QACxD,CAAC;IACH,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meshconnect/uwc-wallet-connect-connector",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "WalletConnect connector for Universal Wallet Connector",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"src"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
+
"@solana/spl-token": "^0.4.13",
|
|
19
20
|
"@solana/web3.js": "^1.98.4",
|
|
20
21
|
"@walletconnect/sign-client": "^2.21.8",
|
|
21
22
|
"@walletconnect/types": "^2.21.8",
|
|
@@ -23,7 +24,7 @@
|
|
|
23
24
|
"bs58": "^6.0.0",
|
|
24
25
|
"buffer": "^6.0.3",
|
|
25
26
|
"ethers": "^6.15.0",
|
|
26
|
-
"@meshconnect/uwc-types": "0.2.
|
|
27
|
+
"@meshconnect/uwc-types": "0.2.1"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
30
|
"typescript": "^5.9.2"
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { ethers } from 'ethers'
|
|
2
|
+
import type {
|
|
3
|
+
EVMGasConfig,
|
|
4
|
+
EVMNativeTransferRequest,
|
|
5
|
+
EVMContractCallRequest
|
|
6
|
+
} from '@meshconnect/uwc-types'
|
|
7
|
+
import { parseError } from '../../utils/error-utils'
|
|
8
|
+
import type { TransactionRequest } from 'ethers'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Service for handling Ethereum/EVM transaction operations
|
|
12
|
+
*/
|
|
13
|
+
export class EthereumTransactionBuilder {
|
|
14
|
+
async getContractTransaction(
|
|
15
|
+
request: EVMContractCallRequest
|
|
16
|
+
): Promise<TransactionRequest> {
|
|
17
|
+
try {
|
|
18
|
+
// Build transaction options
|
|
19
|
+
const txOptions: ethers.Overrides = {
|
|
20
|
+
...this.formatGasConfig(request.gasConfig)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Add value for payable functions
|
|
24
|
+
if (request.value) {
|
|
25
|
+
txOptions.value = request.value
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const iface = new ethers.Interface(request.abi)
|
|
29
|
+
|
|
30
|
+
const data = iface.encodeFunctionData(
|
|
31
|
+
request.functionName,
|
|
32
|
+
request.args || []
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
const txRequest: TransactionRequest = {
|
|
36
|
+
to: request.contractAddress,
|
|
37
|
+
data,
|
|
38
|
+
value: request.value ?? 0n,
|
|
39
|
+
...txOptions
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return txRequest
|
|
43
|
+
} catch (error) {
|
|
44
|
+
parseError(error)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async getNativeTransaction(
|
|
49
|
+
request: EVMNativeTransferRequest
|
|
50
|
+
): Promise<TransactionRequest> {
|
|
51
|
+
try {
|
|
52
|
+
return {
|
|
53
|
+
to: request.to,
|
|
54
|
+
value: request.amount,
|
|
55
|
+
...this.formatGasConfig(request.gasConfig)
|
|
56
|
+
}
|
|
57
|
+
} catch (error) {
|
|
58
|
+
parseError(error)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Helper to format gas configuration for ethers
|
|
64
|
+
*/
|
|
65
|
+
private formatGasConfig(gasConfig?: EVMGasConfig): ethers.Overrides {
|
|
66
|
+
const txOptions: ethers.Overrides = {}
|
|
67
|
+
|
|
68
|
+
if (gasConfig?.gasLimit) {
|
|
69
|
+
txOptions.gasLimit = BigInt(Math.floor(gasConfig.gasLimit))
|
|
70
|
+
}
|
|
71
|
+
if (gasConfig?.maxFeePerGas) {
|
|
72
|
+
txOptions.maxFeePerGas = BigInt(Math.floor(gasConfig.maxFeePerGas))
|
|
73
|
+
}
|
|
74
|
+
if (gasConfig?.maxPriorityFeePerGas) {
|
|
75
|
+
txOptions.maxPriorityFeePerGas = BigInt(
|
|
76
|
+
Math.floor(gasConfig.maxPriorityFeePerGas)
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return txOptions
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
EthereumTransactionRequest,
|
|
3
|
+
EVMContractCallRequest,
|
|
4
|
+
EVMNativeTransferRequest,
|
|
5
|
+
NetworkId
|
|
6
|
+
} from '@meshconnect/uwc-types'
|
|
7
|
+
import type SignClient from '@walletconnect/sign-client'
|
|
8
|
+
import type { SessionTypes } from '@walletconnect/types'
|
|
9
|
+
import { getChainIdFromNetworkId } from '../../utils/account-utils'
|
|
10
|
+
import { parseError } from '../../utils/error-utils'
|
|
11
|
+
import { EthereumTransactionBuilder } from './ethereum-transaction-builder'
|
|
12
|
+
import type { TransactionRequest } from 'ethers'
|
|
13
|
+
|
|
14
|
+
export class EthereumTransactionService {
|
|
15
|
+
private builder: EthereumTransactionBuilder
|
|
16
|
+
constructor() {
|
|
17
|
+
this.builder = new EthereumTransactionBuilder()
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Send an Ethereum transaction
|
|
21
|
+
*/
|
|
22
|
+
async sendTransaction(
|
|
23
|
+
client: SignClient,
|
|
24
|
+
session: SessionTypes.Struct,
|
|
25
|
+
request: EthereumTransactionRequest,
|
|
26
|
+
activeNetworkId: string
|
|
27
|
+
): Promise<string> {
|
|
28
|
+
try {
|
|
29
|
+
// Handle different types of EVM transactions
|
|
30
|
+
if ('contractAddress' in request && 'abi' in request) {
|
|
31
|
+
// Contract call
|
|
32
|
+
return await this.sendContractCall(
|
|
33
|
+
client,
|
|
34
|
+
session,
|
|
35
|
+
request as EVMContractCallRequest,
|
|
36
|
+
activeNetworkId
|
|
37
|
+
)
|
|
38
|
+
} else if ('calls' in request) {
|
|
39
|
+
// Batch transaction - not yet supported
|
|
40
|
+
throw new Error(
|
|
41
|
+
'Batch transactions are not yet supported for WalletConnect'
|
|
42
|
+
)
|
|
43
|
+
} else if ('amount' in request && typeof request.amount === 'bigint') {
|
|
44
|
+
// Native transfer
|
|
45
|
+
return await this.sendNativeTransaction(
|
|
46
|
+
client,
|
|
47
|
+
session,
|
|
48
|
+
request as EVMNativeTransferRequest,
|
|
49
|
+
activeNetworkId
|
|
50
|
+
)
|
|
51
|
+
} else {
|
|
52
|
+
throw new Error('Invalid transaction request type')
|
|
53
|
+
}
|
|
54
|
+
} catch (error) {
|
|
55
|
+
throw parseError(error)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Send native token transfer (ETH, BNB, MATIC, etc.)
|
|
61
|
+
*/
|
|
62
|
+
private async sendNativeTransaction(
|
|
63
|
+
client: SignClient,
|
|
64
|
+
session: SessionTypes.Struct,
|
|
65
|
+
request: EVMNativeTransferRequest,
|
|
66
|
+
activeNetworkId: string
|
|
67
|
+
): Promise<string> {
|
|
68
|
+
try {
|
|
69
|
+
// Use the active network chainId
|
|
70
|
+
const chainId = getChainIdFromNetworkId(activeNetworkId as NetworkId)
|
|
71
|
+
|
|
72
|
+
const params = this.parseTransaction(
|
|
73
|
+
await this.builder.getNativeTransaction(request)
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
const hash = await client.request({
|
|
77
|
+
topic: session.topic,
|
|
78
|
+
chainId: `eip155:${chainId}`,
|
|
79
|
+
request: {
|
|
80
|
+
method: 'eth_sendTransaction',
|
|
81
|
+
params: [params]
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
return hash as string
|
|
86
|
+
} catch (error) {
|
|
87
|
+
throw parseError(error)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Send smart contract transaction (including ERC-20 transfers)
|
|
93
|
+
*/
|
|
94
|
+
private async sendContractCall(
|
|
95
|
+
client: SignClient,
|
|
96
|
+
session: SessionTypes.Struct,
|
|
97
|
+
request: EVMContractCallRequest,
|
|
98
|
+
activeNetworkId: string
|
|
99
|
+
): Promise<string> {
|
|
100
|
+
try {
|
|
101
|
+
// Use the active network chainId
|
|
102
|
+
const chainId = getChainIdFromNetworkId(activeNetworkId as NetworkId)
|
|
103
|
+
|
|
104
|
+
const params = this.parseTransaction(
|
|
105
|
+
await this.builder.getContractTransaction(request)
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
const hash = await client.request({
|
|
109
|
+
topic: session.topic,
|
|
110
|
+
chainId: `eip155:${chainId}`,
|
|
111
|
+
request: {
|
|
112
|
+
method: 'eth_sendTransaction',
|
|
113
|
+
params: [params]
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
return hash as string
|
|
118
|
+
} catch (error) {
|
|
119
|
+
throw parseError(error)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private parseTransaction(tx: TransactionRequest): Record<string, string> {
|
|
124
|
+
// Convert to hex format for WalletConnect
|
|
125
|
+
const txParams: Record<string, string> = {
|
|
126
|
+
from: tx.from as string,
|
|
127
|
+
to: tx.to as string,
|
|
128
|
+
data: (tx.data ?? '0x') as string
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Add value if present
|
|
132
|
+
if (tx.value) {
|
|
133
|
+
txParams['value'] = '0x' + BigInt(tx.value).toString(16)
|
|
134
|
+
} else {
|
|
135
|
+
txParams['value'] = '0x'
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Add gas params if present
|
|
139
|
+
if (tx.gasLimit) {
|
|
140
|
+
txParams['gasLimit'] = '0x' + tx.gasLimit.toString(16)
|
|
141
|
+
}
|
|
142
|
+
if (tx.maxFeePerGas) {
|
|
143
|
+
txParams['maxFeePerGas'] = '0x' + tx.maxFeePerGas.toString(16)
|
|
144
|
+
}
|
|
145
|
+
if (tx.maxPriorityFeePerGas) {
|
|
146
|
+
txParams['maxPriorityFeePerGas'] =
|
|
147
|
+
'0x' + tx.maxPriorityFeePerGas.toString(16)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return txParams
|
|
151
|
+
}
|
|
152
|
+
}
|