@meshconnect/uwc-wallet-connect-connector 0.2.13 → 0.3.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/dist/services/ethereum/ethereum-transaction-builder.d.ts +2 -1
- package/dist/services/ethereum/ethereum-transaction-builder.d.ts.map +1 -1
- package/dist/services/ethereum/ethereum-transaction-builder.js +15 -0
- package/dist/services/ethereum/ethereum-transaction-builder.js.map +1 -1
- package/dist/services/ethereum/ethereum-transaction-service.d.ts +4 -0
- package/dist/services/ethereum/ethereum-transaction-service.d.ts.map +1 -1
- package/dist/services/ethereum/ethereum-transaction-service.js +26 -0
- package/dist/services/ethereum/ethereum-transaction-service.js.map +1 -1
- package/package.json +2 -2
- package/src/services/ethereum/ethereum-transaction-builder.test.ts +286 -0
- package/src/services/ethereum/ethereum-transaction-builder.ts +20 -1
- package/src/services/ethereum/ethereum-transaction-service.test.ts +450 -0
- package/src/services/ethereum/ethereum-transaction-service.ts +41 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EVMNativeTransferRequest, EVMContractCallRequest } from '@meshconnect/uwc-types';
|
|
1
|
+
import type { EVMNativeTransferRequest, EVMContractCallRequest, EVMDataTransferRequest } from '@meshconnect/uwc-types';
|
|
2
2
|
import type { TransactionRequest } from 'ethers';
|
|
3
3
|
/**
|
|
4
4
|
* Service for handling Ethereum/EVM transaction operations
|
|
@@ -6,6 +6,7 @@ import type { TransactionRequest } from 'ethers';
|
|
|
6
6
|
export declare class EthereumTransactionBuilder {
|
|
7
7
|
getContractTransaction(request: EVMContractCallRequest): Promise<TransactionRequest>;
|
|
8
8
|
getNativeTransaction(request: EVMNativeTransferRequest): Promise<TransactionRequest>;
|
|
9
|
+
getDataTransaction(request: EVMDataTransferRequest): Promise<TransactionRequest>;
|
|
9
10
|
/**
|
|
10
11
|
* Helper to format gas configuration for ethers
|
|
11
12
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ethereum-transaction-builder.d.ts","sourceRoot":"","sources":["../../../src/services/ethereum/ethereum-transaction-builder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,wBAAwB,EACxB,sBAAsB,EACvB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAA;AAEhD;;GAEG;AACH,qBAAa,0BAA0B;IAC/B,sBAAsB,CAC1B,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,kBAAkB,CAAC;IAiCxB,oBAAoB,CACxB,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"ethereum-transaction-builder.d.ts","sourceRoot":"","sources":["../../../src/services/ethereum/ethereum-transaction-builder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,wBAAwB,EACxB,sBAAsB,EACtB,sBAAsB,EACvB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAA;AAEhD;;GAEG;AACH,qBAAa,0BAA0B;IAC/B,sBAAsB,CAC1B,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,kBAAkB,CAAC;IAiCxB,oBAAoB,CACxB,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,kBAAkB,CAAC;IAaxB,kBAAkB,CACtB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,kBAAkB,CAAC;IAgB9B;;OAEG;IACH,OAAO,CAAC,eAAe;CAiBxB"}
|
|
@@ -42,6 +42,21 @@ export class EthereumTransactionBuilder {
|
|
|
42
42
|
parseError(error);
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
+
async getDataTransaction(request) {
|
|
46
|
+
try {
|
|
47
|
+
const txRequest = {
|
|
48
|
+
from: request.from,
|
|
49
|
+
to: request.to,
|
|
50
|
+
data: request.data,
|
|
51
|
+
chainId: BigInt(request.chainId),
|
|
52
|
+
...this.formatGasConfig(request.gasConfig)
|
|
53
|
+
};
|
|
54
|
+
return txRequest;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
parseError(error);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
45
60
|
/**
|
|
46
61
|
* Helper to format gas configuration for ethers
|
|
47
62
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ethereum-transaction-builder.js","sourceRoot":"","sources":["../../../src/services/ethereum/ethereum-transaction-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"ethereum-transaction-builder.js","sourceRoot":"","sources":["../../../src/services/ethereum/ethereum-transaction-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,6 +16,10 @@ export declare class EthereumTransactionService {
|
|
|
16
16
|
* Send smart contract transaction (including ERC-20 transfers)
|
|
17
17
|
*/
|
|
18
18
|
private sendContractCall;
|
|
19
|
+
/**
|
|
20
|
+
* Send transaction with raw data
|
|
21
|
+
*/
|
|
22
|
+
private sendDataTransfer;
|
|
19
23
|
private parseTransaction;
|
|
20
24
|
}
|
|
21
25
|
//# sourceMappingURL=ethereum-transaction-service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ethereum-transaction-service.d.ts","sourceRoot":"","sources":["../../../src/services/ethereum/ethereum-transaction-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0BAA0B,
|
|
1
|
+
{"version":3,"file":"ethereum-transaction-service.d.ts","sourceRoot":"","sources":["../../../src/services/ethereum/ethereum-transaction-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0BAA0B,EAK3B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,UAAU,MAAM,4BAA4B,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAMxD,qBAAa,0BAA0B;IACrC,OAAO,CAAC,OAAO,CAA4B;;IAI3C;;OAEG;IACG,eAAe,CACnB,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,YAAY,CAAC,MAAM,EAC5B,OAAO,EAAE,0BAA0B,EACnC,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,MAAM,CAAC;IAwClB;;OAEG;YACW,qBAAqB;IA6BnC;;OAEG;YACW,gBAAgB;IA6B9B;;OAEG;YACW,gBAAgB;IA6B9B,OAAO,CAAC,gBAAgB;CA6BzB"}
|
|
@@ -20,6 +20,10 @@ export class EthereumTransactionService {
|
|
|
20
20
|
// Batch transaction - not yet supported
|
|
21
21
|
throw new Error('Batch transactions are not yet supported for WalletConnect');
|
|
22
22
|
}
|
|
23
|
+
else if ('data' in request && 'chainId' in request) {
|
|
24
|
+
// Data transfer (raw transaction data)
|
|
25
|
+
return await this.sendDataTransfer(client, session, request, activeNetworkId);
|
|
26
|
+
}
|
|
23
27
|
else if ('amount' in request && typeof request.amount === 'bigint') {
|
|
24
28
|
// Native transfer
|
|
25
29
|
return await this.sendNativeTransaction(client, session, request, activeNetworkId);
|
|
@@ -76,6 +80,28 @@ export class EthereumTransactionService {
|
|
|
76
80
|
throw parseError(error);
|
|
77
81
|
}
|
|
78
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Send transaction with raw data
|
|
85
|
+
*/
|
|
86
|
+
async sendDataTransfer(client, session, request, activeNetworkId) {
|
|
87
|
+
try {
|
|
88
|
+
// Use the active network chainId
|
|
89
|
+
const chainId = getChainIdFromNetworkId(activeNetworkId);
|
|
90
|
+
const params = this.parseTransaction(await this.builder.getDataTransaction(request));
|
|
91
|
+
const hash = await client.request({
|
|
92
|
+
topic: session.topic,
|
|
93
|
+
chainId: `eip155:${chainId}`,
|
|
94
|
+
request: {
|
|
95
|
+
method: 'eth_sendTransaction',
|
|
96
|
+
params: [params]
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
return hash;
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
throw parseError(error);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
79
105
|
parseTransaction(tx) {
|
|
80
106
|
// Convert to hex format for WalletConnect
|
|
81
107
|
const txParams = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ethereum-transaction-service.js","sourceRoot":"","sources":["../../../src/services/ethereum/ethereum-transaction-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ethereum-transaction-service.js","sourceRoot":"","sources":["../../../src/services/ethereum/ethereum-transaction-service.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAA;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAA;AAG3E,MAAM,OAAO,0BAA0B;IAC7B,OAAO,CAA4B;IAC3C;QACE,IAAI,CAAC,OAAO,GAAG,IAAI,0BAA0B,EAAE,CAAA;IACjD,CAAC;IACD;;OAEG;IACH,KAAK,CAAC,eAAe,CACnB,MAAkB,EAClB,OAA4B,EAC5B,OAAmC,EACnC,eAAuB;QAEvB,IAAI,CAAC;YACH,6CAA6C;YAC7C,IAAI,iBAAiB,IAAI,OAAO,IAAI,KAAK,IAAI,OAAO,EAAE,CAAC;gBACrD,gBAAgB;gBAChB,OAAO,MAAM,IAAI,CAAC,gBAAgB,CAChC,MAAM,EACN,OAAO,EACP,OAAiC,EACjC,eAAe,CAChB,CAAA;YACH,CAAC;iBAAM,IAAI,OAAO,IAAI,OAAO,EAAE,CAAC;gBAC9B,wCAAwC;gBACxC,MAAM,IAAI,KAAK,CACb,4DAA4D,CAC7D,CAAA;YACH,CAAC;iBAAM,IAAI,MAAM,IAAI,OAAO,IAAI,SAAS,IAAI,OAAO,EAAE,CAAC;gBACrD,uCAAuC;gBACvC,OAAO,MAAM,IAAI,CAAC,gBAAgB,CAChC,MAAM,EACN,OAAO,EACP,OAAiC,EACjC,eAAe,CAChB,CAAA;YACH,CAAC;iBAAM,IAAI,QAAQ,IAAI,OAAO,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;gBACrE,kBAAkB;gBAClB,OAAO,MAAM,IAAI,CAAC,qBAAqB,CACrC,MAAM,EACN,OAAO,EACP,OAAmC,EACnC,eAAe,CAChB,CAAA;YACH,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAA;YACrD,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,UAAU,CAAC,KAAK,CAAC,CAAA;QACzB,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,qBAAqB,CACjC,MAAkB,EAClB,OAA4B,EAC5B,OAAiC,EACjC,eAAuB;QAEvB,IAAI,CAAC;YACH,iCAAiC;YACjC,MAAM,OAAO,GAAG,uBAAuB,CAAC,eAA4B,CAAC,CAAA;YAErE,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,CAClC,MAAM,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC,OAAO,CAAC,CACjD,CAAA;YAED,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;gBAChC,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,OAAO,EAAE,UAAU,OAAO,EAAE;gBAC5B,OAAO,EAAE;oBACP,MAAM,EAAE,qBAAqB;oBAC7B,MAAM,EAAE,CAAC,MAAM,CAAC;iBACjB;aACF,CAAC,CAAA;YAEF,OAAO,IAAc,CAAA;QACvB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,UAAU,CAAC,KAAK,CAAC,CAAA;QACzB,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,gBAAgB,CAC5B,MAAkB,EAClB,OAA4B,EAC5B,OAA+B,EAC/B,eAAuB;QAEvB,IAAI,CAAC;YACH,iCAAiC;YACjC,MAAM,OAAO,GAAG,uBAAuB,CAAC,eAA4B,CAAC,CAAA;YAErE,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,CAClC,MAAM,IAAI,CAAC,OAAO,CAAC,sBAAsB,CAAC,OAAO,CAAC,CACnD,CAAA;YAED,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;gBAChC,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,OAAO,EAAE,UAAU,OAAO,EAAE;gBAC5B,OAAO,EAAE;oBACP,MAAM,EAAE,qBAAqB;oBAC7B,MAAM,EAAE,CAAC,MAAM,CAAC;iBACjB;aACF,CAAC,CAAA;YAEF,OAAO,IAAc,CAAA;QACvB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,UAAU,CAAC,KAAK,CAAC,CAAA;QACzB,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,gBAAgB,CAC5B,MAAkB,EAClB,OAA4B,EAC5B,OAA+B,EAC/B,eAAuB;QAEvB,IAAI,CAAC;YACH,iCAAiC;YACjC,MAAM,OAAO,GAAG,uBAAuB,CAAC,eAA4B,CAAC,CAAA;YAErE,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,CAClC,MAAM,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAC/C,CAAA;YAED,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;gBAChC,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,OAAO,EAAE,UAAU,OAAO,EAAE;gBAC5B,OAAO,EAAE;oBACP,MAAM,EAAE,qBAAqB;oBAC7B,MAAM,EAAE,CAAC,MAAM,CAAC;iBACjB;aACF,CAAC,CAAA;YAEF,OAAO,IAAc,CAAA;QACvB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,UAAU,CAAC,KAAK,CAAC,CAAA;QACzB,CAAC;IACH,CAAC;IAEO,gBAAgB,CAAC,EAAsB;QAC7C,0CAA0C;QAC1C,MAAM,QAAQ,GAA2B;YACvC,IAAI,EAAE,EAAE,CAAC,IAAc;YACvB,EAAE,EAAE,EAAE,CAAC,EAAY;YACnB,IAAI,EAAE,CAAC,EAAE,CAAC,IAAI,IAAI,IAAI,CAAW;SAClC,CAAA;QAED,uBAAuB;QACvB,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC;YACb,QAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;QAC1D,CAAC;aAAM,CAAC;YACN,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAA;QAC1B,CAAC;QAED,4BAA4B;QAC5B,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC;YAChB,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;QACxD,CAAC;QACD,IAAI,EAAE,CAAC,YAAY,EAAE,CAAC;YACpB,QAAQ,CAAC,cAAc,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;QAChE,CAAC;QACD,IAAI,EAAE,CAAC,oBAAoB,EAAE,CAAC;YAC5B,QAAQ,CAAC,sBAAsB,CAAC;gBAC9B,IAAI,GAAG,EAAE,CAAC,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;QAC/C,CAAC;QAED,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meshconnect/uwc-wallet-connect-connector",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "WalletConnect connector for Universal Wallet Connector",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"buffer": "^6.0.3",
|
|
26
26
|
"ethers": "^6.15.0",
|
|
27
27
|
"tronweb": "^6.0.4",
|
|
28
|
-
"@meshconnect/uwc-types": "0.
|
|
28
|
+
"@meshconnect/uwc-types": "0.4.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"typescript": "^5.9.2"
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest'
|
|
2
|
+
import { EthereumTransactionBuilder } from './ethereum-transaction-builder'
|
|
3
|
+
import type {
|
|
4
|
+
EVMNativeTransferRequest,
|
|
5
|
+
EVMContractCallRequest,
|
|
6
|
+
EVMDataTransferRequest
|
|
7
|
+
} from '@meshconnect/uwc-types'
|
|
8
|
+
|
|
9
|
+
describe('WalletConnect EthereumTransactionBuilder', () => {
|
|
10
|
+
let builder: EthereumTransactionBuilder
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
builder = new EthereumTransactionBuilder()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
describe('getNativeTransaction', () => {
|
|
17
|
+
it('should build a native transaction request with from field', async () => {
|
|
18
|
+
const request: EVMNativeTransferRequest = {
|
|
19
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
20
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
21
|
+
amount: BigInt('1000000000000000000'), // 1 ETH
|
|
22
|
+
gasConfig: {
|
|
23
|
+
gasLimit: 21000,
|
|
24
|
+
maxFeePerGas: 50000000000,
|
|
25
|
+
maxPriorityFeePerGas: 2000000000
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const result = await builder.getNativeTransaction(request)
|
|
30
|
+
|
|
31
|
+
expect(result).toEqual({
|
|
32
|
+
from: request.from,
|
|
33
|
+
to: request.to,
|
|
34
|
+
value: request.amount,
|
|
35
|
+
gasLimit: BigInt(21000),
|
|
36
|
+
maxFeePerGas: BigInt(50000000000),
|
|
37
|
+
maxPriorityFeePerGas: BigInt(2000000000)
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('should build a native transaction without gas config', async () => {
|
|
42
|
+
const request: EVMNativeTransferRequest = {
|
|
43
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
44
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
45
|
+
amount: BigInt('500000000000000000') // 0.5 ETH
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const result = await builder.getNativeTransaction(request)
|
|
49
|
+
|
|
50
|
+
expect(result).toEqual({
|
|
51
|
+
from: request.from,
|
|
52
|
+
to: request.to,
|
|
53
|
+
value: request.amount
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
describe('getContractTransaction', () => {
|
|
59
|
+
it('should build a contract transaction request with from field', async () => {
|
|
60
|
+
const erc20ABI = [
|
|
61
|
+
{
|
|
62
|
+
inputs: [
|
|
63
|
+
{ name: 'recipient', type: 'address' },
|
|
64
|
+
{ name: 'amount', type: 'uint256' }
|
|
65
|
+
],
|
|
66
|
+
name: 'transfer',
|
|
67
|
+
outputs: [{ name: '', type: 'bool' }],
|
|
68
|
+
stateMutability: 'nonpayable',
|
|
69
|
+
type: 'function'
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
const request: EVMContractCallRequest = {
|
|
74
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
75
|
+
contractAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
|
76
|
+
abi: erc20ABI,
|
|
77
|
+
functionName: 'transfer',
|
|
78
|
+
args: ['0x0987654321098765432109876543210987654321', BigInt(1000000)],
|
|
79
|
+
gasConfig: {
|
|
80
|
+
gasLimit: 100000
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const result = await builder.getContractTransaction(request)
|
|
85
|
+
|
|
86
|
+
expect(result.from).toBe(request.from)
|
|
87
|
+
expect(result.to).toBe(request.contractAddress)
|
|
88
|
+
expect(result.data).toBeDefined()
|
|
89
|
+
expect(result.data).toContain('0xa9059cbb') // transfer function selector
|
|
90
|
+
expect(result.value).toBe(0n)
|
|
91
|
+
expect(result.gasLimit).toBe(BigInt(100000))
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('should build a payable contract transaction with value', async () => {
|
|
95
|
+
const payableABI = [
|
|
96
|
+
{
|
|
97
|
+
inputs: [],
|
|
98
|
+
name: 'deposit',
|
|
99
|
+
outputs: [],
|
|
100
|
+
stateMutability: 'payable',
|
|
101
|
+
type: 'function'
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
const request: EVMContractCallRequest = {
|
|
106
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
107
|
+
contractAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
|
108
|
+
abi: payableABI,
|
|
109
|
+
functionName: 'deposit',
|
|
110
|
+
args: [],
|
|
111
|
+
value: BigInt('1000000000000000000') // 1 ETH
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const result = await builder.getContractTransaction(request)
|
|
115
|
+
|
|
116
|
+
expect(result.from).toBe(request.from)
|
|
117
|
+
expect(result.to).toBe(request.contractAddress)
|
|
118
|
+
expect(result.data).toBeDefined()
|
|
119
|
+
expect(result.value).toBe(request.value)
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
describe('getDataTransaction', () => {
|
|
124
|
+
it('should build a data transaction request with all fields', async () => {
|
|
125
|
+
const request: EVMDataTransferRequest = {
|
|
126
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
127
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
128
|
+
data: '0xa9059cbb000000000000000000000000123456789012345678901234567890123456789000000000000000000000000000000000000000000000000000000000000f4240',
|
|
129
|
+
chainId: '1',
|
|
130
|
+
value: BigInt('500000000000000000'), // 0.5 ETH
|
|
131
|
+
gasConfig: {
|
|
132
|
+
gasLimit: 100000,
|
|
133
|
+
maxFeePerGas: 50000000000,
|
|
134
|
+
maxPriorityFeePerGas: 2000000000
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const result = await builder.getDataTransaction(request)
|
|
139
|
+
|
|
140
|
+
expect(result).toEqual({
|
|
141
|
+
from: request.from,
|
|
142
|
+
to: request.to,
|
|
143
|
+
data: request.data,
|
|
144
|
+
chainId: BigInt(1),
|
|
145
|
+
gasLimit: BigInt(100000),
|
|
146
|
+
maxFeePerGas: BigInt(50000000000),
|
|
147
|
+
maxPriorityFeePerGas: BigInt(2000000000)
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('should build a data transaction without value', async () => {
|
|
152
|
+
const request: EVMDataTransferRequest = {
|
|
153
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
154
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
155
|
+
data: '0xa9059cbb',
|
|
156
|
+
chainId: '137' // Polygon
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const result = await builder.getDataTransaction(request)
|
|
160
|
+
|
|
161
|
+
expect(result).toEqual({
|
|
162
|
+
from: request.from,
|
|
163
|
+
to: request.to,
|
|
164
|
+
data: request.data,
|
|
165
|
+
chainId: BigInt(137)
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('should build a data transaction with partial gas config', async () => {
|
|
170
|
+
const request: EVMDataTransferRequest = {
|
|
171
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
172
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
173
|
+
data: '0x00',
|
|
174
|
+
chainId: '8453', // Base
|
|
175
|
+
gasConfig: {
|
|
176
|
+
gasLimit: 50000
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const result = await builder.getDataTransaction(request)
|
|
181
|
+
|
|
182
|
+
expect(result.from).toBe(request.from)
|
|
183
|
+
expect(result.gasLimit).toBe(BigInt(50000))
|
|
184
|
+
expect(result.maxFeePerGas).toBeUndefined()
|
|
185
|
+
expect(result.maxPriorityFeePerGas).toBeUndefined()
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('should handle empty data field', async () => {
|
|
189
|
+
const request: EVMDataTransferRequest = {
|
|
190
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
191
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
192
|
+
data: '0x',
|
|
193
|
+
chainId: '1'
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const result = await builder.getDataTransaction(request)
|
|
197
|
+
|
|
198
|
+
expect(result.from).toBe(request.from)
|
|
199
|
+
expect(result.data).toBe('0x')
|
|
200
|
+
expect(result.to).toBe(request.to)
|
|
201
|
+
expect(result.chainId).toBe(BigInt(1))
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it('should handle complex encoded data', async () => {
|
|
205
|
+
const complexData = '0x' + 'a'.repeat(1000) // Long hex string
|
|
206
|
+
|
|
207
|
+
const request: EVMDataTransferRequest = {
|
|
208
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
209
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
210
|
+
data: complexData,
|
|
211
|
+
chainId: '42161' // Arbitrum
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const result = await builder.getDataTransaction(request)
|
|
215
|
+
|
|
216
|
+
expect(result.from).toBe(request.from)
|
|
217
|
+
expect(result.data).toBe(complexData)
|
|
218
|
+
expect(result.chainId).toBe(BigInt(42161))
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
it('should handle different chain IDs', async () => {
|
|
222
|
+
const chains = [
|
|
223
|
+
{ id: '1', name: 'Ethereum' },
|
|
224
|
+
{ id: '137', name: 'Polygon' },
|
|
225
|
+
{ id: '8453', name: 'Base' },
|
|
226
|
+
{ id: '42161', name: 'Arbitrum' },
|
|
227
|
+
{ id: '10', name: 'Optimism' }
|
|
228
|
+
]
|
|
229
|
+
|
|
230
|
+
for (const chain of chains) {
|
|
231
|
+
const request: EVMDataTransferRequest = {
|
|
232
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
233
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
234
|
+
data: '0xa9059cbb',
|
|
235
|
+
chainId: chain.id
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const result = await builder.getDataTransaction(request)
|
|
239
|
+
|
|
240
|
+
expect(result.chainId).toBe(BigInt(chain.id))
|
|
241
|
+
}
|
|
242
|
+
})
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
describe('formatGasConfig', () => {
|
|
246
|
+
it('should handle null gas config values', async () => {
|
|
247
|
+
const request: EVMDataTransferRequest = {
|
|
248
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
249
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
250
|
+
data: '0x00',
|
|
251
|
+
chainId: '1',
|
|
252
|
+
gasConfig: {
|
|
253
|
+
gasLimit: null,
|
|
254
|
+
maxFeePerGas: null,
|
|
255
|
+
maxPriorityFeePerGas: null
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const result = await builder.getDataTransaction(request)
|
|
260
|
+
|
|
261
|
+
expect(result.gasLimit).toBeUndefined()
|
|
262
|
+
expect(result.maxFeePerGas).toBeUndefined()
|
|
263
|
+
expect(result.maxPriorityFeePerGas).toBeUndefined()
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it('should floor decimal gas values', async () => {
|
|
267
|
+
const request: EVMDataTransferRequest = {
|
|
268
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
269
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
270
|
+
data: '0x00',
|
|
271
|
+
chainId: '1',
|
|
272
|
+
gasConfig: {
|
|
273
|
+
gasLimit: 100000.7,
|
|
274
|
+
maxFeePerGas: 50000000000.9,
|
|
275
|
+
maxPriorityFeePerGas: 2000000000.1
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const result = await builder.getDataTransaction(request)
|
|
280
|
+
|
|
281
|
+
expect(result.gasLimit).toBe(BigInt(100000))
|
|
282
|
+
expect(result.maxFeePerGas).toBe(BigInt(50000000000))
|
|
283
|
+
expect(result.maxPriorityFeePerGas).toBe(BigInt(2000000000))
|
|
284
|
+
})
|
|
285
|
+
})
|
|
286
|
+
})
|
|
@@ -2,7 +2,8 @@ import { ethers } from 'ethers'
|
|
|
2
2
|
import type {
|
|
3
3
|
EVMGasConfig,
|
|
4
4
|
EVMNativeTransferRequest,
|
|
5
|
-
EVMContractCallRequest
|
|
5
|
+
EVMContractCallRequest,
|
|
6
|
+
EVMDataTransferRequest
|
|
6
7
|
} from '@meshconnect/uwc-types'
|
|
7
8
|
import { parseError } from '../../utils/error-utils'
|
|
8
9
|
import type { TransactionRequest } from 'ethers'
|
|
@@ -61,6 +62,24 @@ export class EthereumTransactionBuilder {
|
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
async getDataTransaction(
|
|
66
|
+
request: EVMDataTransferRequest
|
|
67
|
+
): Promise<TransactionRequest> {
|
|
68
|
+
try {
|
|
69
|
+
const txRequest: TransactionRequest = {
|
|
70
|
+
from: request.from,
|
|
71
|
+
to: request.to,
|
|
72
|
+
data: request.data,
|
|
73
|
+
chainId: BigInt(request.chainId),
|
|
74
|
+
...this.formatGasConfig(request.gasConfig)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return txRequest
|
|
78
|
+
} catch (error) {
|
|
79
|
+
parseError(error)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
64
83
|
/**
|
|
65
84
|
* Helper to format gas configuration for ethers
|
|
66
85
|
*/
|
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
2
|
+
import { EthereumTransactionService } from './ethereum-transaction-service'
|
|
3
|
+
import type {
|
|
4
|
+
EVMNativeTransferRequest,
|
|
5
|
+
EVMContractCallRequest,
|
|
6
|
+
EVMDataTransferRequest,
|
|
7
|
+
NetworkId
|
|
8
|
+
} from '@meshconnect/uwc-types'
|
|
9
|
+
import type SignClient from '@walletconnect/sign-client'
|
|
10
|
+
import type { SessionTypes } from '@walletconnect/types'
|
|
11
|
+
|
|
12
|
+
// Mock dependencies
|
|
13
|
+
vi.mock('../../utils/account-utils', () => ({
|
|
14
|
+
getChainIdFromNetworkId: vi.fn((networkId: NetworkId) => {
|
|
15
|
+
const chainMap: Record<string, number> = {
|
|
16
|
+
'eip155:1': 1,
|
|
17
|
+
'eip155:137': 137,
|
|
18
|
+
'eip155:8453': 8453,
|
|
19
|
+
'eip155:42161': 42161,
|
|
20
|
+
'eip155:10': 10
|
|
21
|
+
}
|
|
22
|
+
return chainMap[networkId] || 1
|
|
23
|
+
})
|
|
24
|
+
}))
|
|
25
|
+
|
|
26
|
+
vi.mock('ethers', () => ({
|
|
27
|
+
ethers: {
|
|
28
|
+
Interface: vi.fn().mockImplementation(() => ({
|
|
29
|
+
encodeFunctionData: vi.fn().mockReturnValue('0xencodeddata')
|
|
30
|
+
}))
|
|
31
|
+
}
|
|
32
|
+
}))
|
|
33
|
+
|
|
34
|
+
describe('WalletConnect EthereumTransactionService', () => {
|
|
35
|
+
let service: EthereumTransactionService
|
|
36
|
+
let mockClient: SignClient
|
|
37
|
+
let mockSession: SessionTypes.Struct
|
|
38
|
+
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
vi.clearAllMocks()
|
|
41
|
+
service = new EthereumTransactionService()
|
|
42
|
+
|
|
43
|
+
mockClient = {
|
|
44
|
+
request: vi.fn().mockResolvedValue('0xtxhash')
|
|
45
|
+
} as any
|
|
46
|
+
|
|
47
|
+
mockSession = {
|
|
48
|
+
topic: 'test-topic',
|
|
49
|
+
pairingTopic: 'pairing-topic',
|
|
50
|
+
relay: { protocol: 'irn' },
|
|
51
|
+
expiry: Date.now() + 86400,
|
|
52
|
+
acknowledged: true,
|
|
53
|
+
controller: 'test-controller',
|
|
54
|
+
namespaces: {},
|
|
55
|
+
requiredNamespaces: {},
|
|
56
|
+
optionalNamespaces: {},
|
|
57
|
+
self: {
|
|
58
|
+
publicKey: 'test-public-key',
|
|
59
|
+
metadata: {
|
|
60
|
+
name: 'Test dApp',
|
|
61
|
+
description: 'Test',
|
|
62
|
+
url: 'https://test.com',
|
|
63
|
+
icons: []
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
peer: {
|
|
67
|
+
publicKey: 'peer-public-key',
|
|
68
|
+
metadata: {
|
|
69
|
+
name: 'Test Wallet',
|
|
70
|
+
description: 'Test',
|
|
71
|
+
url: 'https://wallet.com',
|
|
72
|
+
icons: []
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
describe('sendTransaction routing', () => {
|
|
79
|
+
it('should route native transfer to sendNativeTransaction', async () => {
|
|
80
|
+
const request: EVMNativeTransferRequest = {
|
|
81
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
82
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
83
|
+
amount: BigInt('1000000000000000000'),
|
|
84
|
+
gasConfig: { gasLimit: 21000 }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const result = await service.sendTransaction(
|
|
88
|
+
mockClient,
|
|
89
|
+
mockSession,
|
|
90
|
+
request,
|
|
91
|
+
'eip155:1'
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
expect(result).toBe('0xtxhash')
|
|
95
|
+
expect(mockClient.request).toHaveBeenCalledWith(
|
|
96
|
+
expect.objectContaining({
|
|
97
|
+
topic: mockSession.topic,
|
|
98
|
+
chainId: 'eip155:1'
|
|
99
|
+
})
|
|
100
|
+
)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('should route contract call to sendContractCall', async () => {
|
|
104
|
+
const request: EVMContractCallRequest = {
|
|
105
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
106
|
+
contractAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
|
107
|
+
abi: [
|
|
108
|
+
{
|
|
109
|
+
inputs: [
|
|
110
|
+
{ name: 'recipient', type: 'address' },
|
|
111
|
+
{ name: 'amount', type: 'uint256' }
|
|
112
|
+
],
|
|
113
|
+
name: 'transfer',
|
|
114
|
+
outputs: [{ name: '', type: 'bool' }],
|
|
115
|
+
stateMutability: 'nonpayable',
|
|
116
|
+
type: 'function'
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
functionName: 'transfer',
|
|
120
|
+
args: ['0x0987654321098765432109876543210987654321', BigInt(1000000)]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const result = await service.sendTransaction(
|
|
124
|
+
mockClient,
|
|
125
|
+
mockSession,
|
|
126
|
+
request,
|
|
127
|
+
'eip155:1'
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
expect(result).toBe('0xtxhash')
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('should route data transfer to sendDataTransfer', async () => {
|
|
134
|
+
const request: EVMDataTransferRequest = {
|
|
135
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
136
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
137
|
+
data: '0xa9059cbb',
|
|
138
|
+
chainId: '1'
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const result = await service.sendTransaction(
|
|
142
|
+
mockClient,
|
|
143
|
+
mockSession,
|
|
144
|
+
request,
|
|
145
|
+
'eip155:1'
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
expect(result).toBe('0xtxhash')
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('should throw error for batch transactions', async () => {
|
|
152
|
+
const request = {
|
|
153
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
154
|
+
calls: []
|
|
155
|
+
} as any
|
|
156
|
+
|
|
157
|
+
await expect(
|
|
158
|
+
service.sendTransaction(mockClient, mockSession, request, 'eip155:1')
|
|
159
|
+
).rejects.toThrow('Batch transactions are not yet supported')
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('should throw error for invalid request type', async () => {
|
|
163
|
+
const invalidRequest = {
|
|
164
|
+
from: '0x1234567890123456789012345678901234567890'
|
|
165
|
+
} as any
|
|
166
|
+
|
|
167
|
+
await expect(
|
|
168
|
+
service.sendTransaction(
|
|
169
|
+
mockClient,
|
|
170
|
+
mockSession,
|
|
171
|
+
invalidRequest,
|
|
172
|
+
'eip155:1'
|
|
173
|
+
)
|
|
174
|
+
).rejects.toThrow('Invalid transaction request type')
|
|
175
|
+
})
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
describe('sendDataTransfer', () => {
|
|
179
|
+
it('should send data transfer successfully', async () => {
|
|
180
|
+
const request: EVMDataTransferRequest = {
|
|
181
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
182
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
183
|
+
data: '0xa9059cbb000000000000000000000000123456789012345678901234567890123456789000000000000000000000000000000000000000000000000000000000000f4240',
|
|
184
|
+
chainId: '1'
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const result = await service.sendTransaction(
|
|
188
|
+
mockClient,
|
|
189
|
+
mockSession,
|
|
190
|
+
request,
|
|
191
|
+
'eip155:1'
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
expect(result).toBe('0xtxhash')
|
|
195
|
+
expect(mockClient.request).toHaveBeenCalledWith({
|
|
196
|
+
topic: mockSession.topic,
|
|
197
|
+
chainId: 'eip155:1',
|
|
198
|
+
request: {
|
|
199
|
+
method: 'eth_sendTransaction',
|
|
200
|
+
params: [
|
|
201
|
+
expect.objectContaining({
|
|
202
|
+
from: request.from,
|
|
203
|
+
to: request.to,
|
|
204
|
+
data: request.data
|
|
205
|
+
})
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it('should send data transfer with value', async () => {
|
|
212
|
+
const request: EVMDataTransferRequest = {
|
|
213
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
214
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
215
|
+
data: '0xa9059cbb',
|
|
216
|
+
chainId: '1',
|
|
217
|
+
value: BigInt('500000000000000000')
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const result = await service.sendTransaction(
|
|
221
|
+
mockClient,
|
|
222
|
+
mockSession,
|
|
223
|
+
request,
|
|
224
|
+
'eip155:1'
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
expect(result).toBe('0xtxhash')
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
it('should send data transfer with gas config', async () => {
|
|
231
|
+
const request: EVMDataTransferRequest = {
|
|
232
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
233
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
234
|
+
data: '0x00',
|
|
235
|
+
chainId: '137',
|
|
236
|
+
gasConfig: {
|
|
237
|
+
gasLimit: 100000,
|
|
238
|
+
maxFeePerGas: 50000000000,
|
|
239
|
+
maxPriorityFeePerGas: 2000000000
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const result = await service.sendTransaction(
|
|
244
|
+
mockClient,
|
|
245
|
+
mockSession,
|
|
246
|
+
request,
|
|
247
|
+
'eip155:137'
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
expect(result).toBe('0xtxhash')
|
|
251
|
+
expect(mockClient.request).toHaveBeenCalledWith(
|
|
252
|
+
expect.objectContaining({
|
|
253
|
+
chainId: 'eip155:137'
|
|
254
|
+
})
|
|
255
|
+
)
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
it('should send data transfer on different networks', async () => {
|
|
259
|
+
const networks = [
|
|
260
|
+
'eip155:1',
|
|
261
|
+
'eip155:137',
|
|
262
|
+
'eip155:8453',
|
|
263
|
+
'eip155:42161',
|
|
264
|
+
'eip155:10'
|
|
265
|
+
]
|
|
266
|
+
|
|
267
|
+
for (const networkId of networks) {
|
|
268
|
+
vi.mocked(mockClient.request).mockClear()
|
|
269
|
+
|
|
270
|
+
const request: EVMDataTransferRequest = {
|
|
271
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
272
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
273
|
+
data: '0xa9059cbb',
|
|
274
|
+
chainId: networkId.split(':')[1]
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const result = await service.sendTransaction(
|
|
278
|
+
mockClient,
|
|
279
|
+
mockSession,
|
|
280
|
+
request,
|
|
281
|
+
networkId
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
expect(result).toBe('0xtxhash')
|
|
285
|
+
expect(mockClient.request).toHaveBeenCalledWith(
|
|
286
|
+
expect.objectContaining({
|
|
287
|
+
chainId: networkId
|
|
288
|
+
})
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
it('should handle empty data field', async () => {
|
|
294
|
+
const request: EVMDataTransferRequest = {
|
|
295
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
296
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
297
|
+
data: '0x',
|
|
298
|
+
chainId: '1'
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const result = await service.sendTransaction(
|
|
302
|
+
mockClient,
|
|
303
|
+
mockSession,
|
|
304
|
+
request,
|
|
305
|
+
'eip155:1'
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
expect(result).toBe('0xtxhash')
|
|
309
|
+
expect(mockClient.request).toHaveBeenCalledWith(
|
|
310
|
+
expect.objectContaining({
|
|
311
|
+
request: {
|
|
312
|
+
method: 'eth_sendTransaction',
|
|
313
|
+
params: [
|
|
314
|
+
expect.objectContaining({
|
|
315
|
+
data: '0x'
|
|
316
|
+
})
|
|
317
|
+
]
|
|
318
|
+
}
|
|
319
|
+
})
|
|
320
|
+
)
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
it('should handle complex encoded data', async () => {
|
|
324
|
+
const complexData = '0x' + 'a'.repeat(1000)
|
|
325
|
+
|
|
326
|
+
const request: EVMDataTransferRequest = {
|
|
327
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
328
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
329
|
+
data: complexData,
|
|
330
|
+
chainId: '1'
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const result = await service.sendTransaction(
|
|
334
|
+
mockClient,
|
|
335
|
+
mockSession,
|
|
336
|
+
request,
|
|
337
|
+
'eip155:1'
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
expect(result).toBe('0xtxhash')
|
|
341
|
+
})
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
describe('parseTransaction', () => {
|
|
345
|
+
it('should convert BigInt values to hex format', async () => {
|
|
346
|
+
const request: EVMDataTransferRequest = {
|
|
347
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
348
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
349
|
+
data: '0xa9059cbb',
|
|
350
|
+
chainId: '1',
|
|
351
|
+
value: BigInt('1000000000000000000'),
|
|
352
|
+
gasConfig: {
|
|
353
|
+
gasLimit: 21000,
|
|
354
|
+
maxFeePerGas: 50000000000,
|
|
355
|
+
maxPriorityFeePerGas: 2000000000
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
await service.sendTransaction(
|
|
360
|
+
mockClient,
|
|
361
|
+
mockSession,
|
|
362
|
+
request,
|
|
363
|
+
'eip155:1'
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
expect(mockClient.request).toHaveBeenCalledWith(
|
|
367
|
+
expect.objectContaining({
|
|
368
|
+
request: {
|
|
369
|
+
method: 'eth_sendTransaction',
|
|
370
|
+
params: [
|
|
371
|
+
expect.objectContaining({
|
|
372
|
+
from: request.from,
|
|
373
|
+
to: request.to,
|
|
374
|
+
data: request.data,
|
|
375
|
+
gasLimit: expect.stringMatching(/^0x/),
|
|
376
|
+
maxFeePerGas: expect.stringMatching(/^0x/),
|
|
377
|
+
maxPriorityFeePerGas: expect.stringMatching(/^0x/)
|
|
378
|
+
})
|
|
379
|
+
]
|
|
380
|
+
}
|
|
381
|
+
})
|
|
382
|
+
)
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
it('should handle missing value field', async () => {
|
|
386
|
+
const request: EVMDataTransferRequest = {
|
|
387
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
388
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
389
|
+
data: '0xa9059cbb',
|
|
390
|
+
chainId: '1'
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
await service.sendTransaction(
|
|
394
|
+
mockClient,
|
|
395
|
+
mockSession,
|
|
396
|
+
request,
|
|
397
|
+
'eip155:1'
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
const callArgs = vi.mocked(mockClient.request).mock.calls[0][0]
|
|
401
|
+
const params = (callArgs.request as any).params[0]
|
|
402
|
+
|
|
403
|
+
// Value should either not exist or be undefined/deleted
|
|
404
|
+
expect(params.value === undefined || !('value' in params)).toBe(true)
|
|
405
|
+
})
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
describe('error handling', () => {
|
|
409
|
+
it('should handle WalletConnect request errors', async () => {
|
|
410
|
+
const mockError = new Error('User rejected')
|
|
411
|
+
vi.mocked(mockClient.request).mockRejectedValueOnce(mockError)
|
|
412
|
+
|
|
413
|
+
const request: EVMDataTransferRequest = {
|
|
414
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
415
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
416
|
+
data: '0xa9059cbb',
|
|
417
|
+
chainId: '1'
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
await expect(
|
|
421
|
+
service.sendTransaction(mockClient, mockSession, request, 'eip155:1')
|
|
422
|
+
).rejects.toThrow()
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
it('should handle network mismatch', async () => {
|
|
426
|
+
const request: EVMDataTransferRequest = {
|
|
427
|
+
from: '0x1234567890123456789012345678901234567890',
|
|
428
|
+
to: '0x0987654321098765432109876543210987654321',
|
|
429
|
+
data: '0xa9059cbb',
|
|
430
|
+
chainId: '137' // Polygon
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Send on Ethereum network
|
|
434
|
+
const result = await service.sendTransaction(
|
|
435
|
+
mockClient,
|
|
436
|
+
mockSession,
|
|
437
|
+
request,
|
|
438
|
+
'eip155:1'
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
expect(result).toBe('0xtxhash')
|
|
442
|
+
// Should use the activeNetworkId for chainId in request
|
|
443
|
+
expect(mockClient.request).toHaveBeenCalledWith(
|
|
444
|
+
expect.objectContaining({
|
|
445
|
+
chainId: 'eip155:1'
|
|
446
|
+
})
|
|
447
|
+
)
|
|
448
|
+
})
|
|
449
|
+
})
|
|
450
|
+
})
|
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
EthereumTransactionRequest,
|
|
3
3
|
EVMContractCallRequest,
|
|
4
4
|
EVMNativeTransferRequest,
|
|
5
|
+
EVMDataTransferRequest,
|
|
5
6
|
NetworkId
|
|
6
7
|
} from '@meshconnect/uwc-types'
|
|
7
8
|
import type SignClient from '@walletconnect/sign-client'
|
|
@@ -40,6 +41,14 @@ export class EthereumTransactionService {
|
|
|
40
41
|
throw new Error(
|
|
41
42
|
'Batch transactions are not yet supported for WalletConnect'
|
|
42
43
|
)
|
|
44
|
+
} else if ('data' in request && 'chainId' in request) {
|
|
45
|
+
// Data transfer (raw transaction data)
|
|
46
|
+
return await this.sendDataTransfer(
|
|
47
|
+
client,
|
|
48
|
+
session,
|
|
49
|
+
request as EVMDataTransferRequest,
|
|
50
|
+
activeNetworkId
|
|
51
|
+
)
|
|
43
52
|
} else if ('amount' in request && typeof request.amount === 'bigint') {
|
|
44
53
|
// Native transfer
|
|
45
54
|
return await this.sendNativeTransaction(
|
|
@@ -120,6 +129,38 @@ export class EthereumTransactionService {
|
|
|
120
129
|
}
|
|
121
130
|
}
|
|
122
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Send transaction with raw data
|
|
134
|
+
*/
|
|
135
|
+
private async sendDataTransfer(
|
|
136
|
+
client: SignClient,
|
|
137
|
+
session: SessionTypes.Struct,
|
|
138
|
+
request: EVMDataTransferRequest,
|
|
139
|
+
activeNetworkId: string
|
|
140
|
+
): Promise<string> {
|
|
141
|
+
try {
|
|
142
|
+
// Use the active network chainId
|
|
143
|
+
const chainId = getChainIdFromNetworkId(activeNetworkId as NetworkId)
|
|
144
|
+
|
|
145
|
+
const params = this.parseTransaction(
|
|
146
|
+
await this.builder.getDataTransaction(request)
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
const hash = await client.request({
|
|
150
|
+
topic: session.topic,
|
|
151
|
+
chainId: `eip155:${chainId}`,
|
|
152
|
+
request: {
|
|
153
|
+
method: 'eth_sendTransaction',
|
|
154
|
+
params: [params]
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
return hash as string
|
|
159
|
+
} catch (error) {
|
|
160
|
+
throw parseError(error)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
123
164
|
private parseTransaction(tx: TransactionRequest): Record<string, string> {
|
|
124
165
|
// Convert to hex format for WalletConnect
|
|
125
166
|
const txParams: Record<string, string> = {
|