@hashgraph/hedera-wallet-connect 2.0.4-canary.f71fa76.0 → 2.0.5-canary.09073be.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/README.md +141 -8
- package/package.json +4 -2
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -21
- package/dist/lib/dapp/DAppSigner.d.ts +0 -60
- package/dist/lib/dapp/DAppSigner.js +0 -301
- package/dist/lib/dapp/SessionNotFoundError.d.ts +0 -3
- package/dist/lib/dapp/SessionNotFoundError.js +0 -6
- package/dist/lib/dapp/index.d.ts +0 -216
- package/dist/lib/dapp/index.js +0 -580
- package/dist/lib/index.d.ts +0 -3
- package/dist/lib/index.js +0 -22
- package/dist/lib/shared/chainIds.d.ts +0 -11
- package/dist/lib/shared/chainIds.js +0 -31
- package/dist/lib/shared/errors.d.ts +0 -48
- package/dist/lib/shared/errors.js +0 -51
- package/dist/lib/shared/events.d.ts +0 -7
- package/dist/lib/shared/events.js +0 -27
- package/dist/lib/shared/extensionController.d.ts +0 -21
- package/dist/lib/shared/extensionController.js +0 -42
- package/dist/lib/shared/index.d.ts +0 -7
- package/dist/lib/shared/index.js +0 -26
- package/dist/lib/shared/logger.d.ts +0 -18
- package/dist/lib/shared/logger.js +0 -33
- package/dist/lib/shared/methods.d.ts +0 -12
- package/dist/lib/shared/methods.js +0 -32
- package/dist/lib/shared/payloads.d.ts +0 -101
- package/dist/lib/shared/payloads.js +0 -20
- package/dist/lib/shared/utils.d.ts +0 -275
- package/dist/lib/shared/utils.js +0 -420
- package/dist/lib/wallet/index.d.ts +0 -39
- package/dist/lib/wallet/index.js +0 -306
- package/dist/lib/wallet/provider.d.ts +0 -17
- package/dist/lib/wallet/provider.js +0 -58
- package/dist/lib/wallet/types.d.ts +0 -31
- package/dist/lib/wallet/types.js +0 -20
- package/dist/reown/adapter.d.ts +0 -59
- package/dist/reown/adapter.js +0 -261
- package/dist/reown/connectors/HederaConnector.d.ts +0 -31
- package/dist/reown/connectors/HederaConnector.js +0 -38
- package/dist/reown/connectors/index.d.ts +0 -1
- package/dist/reown/connectors/index.js +0 -1
- package/dist/reown/index.d.ts +0 -4
- package/dist/reown/index.js +0 -4
- package/dist/reown/providers/EIP155Provider.d.ts +0 -32
- package/dist/reown/providers/EIP155Provider.js +0 -187
- package/dist/reown/providers/HIP820Provider.d.ts +0 -26
- package/dist/reown/providers/HIP820Provider.js +0 -69
- package/dist/reown/providers/HederaProvider.d.ts +0 -164
- package/dist/reown/providers/HederaProvider.js +0 -471
- package/dist/reown/providers/index.d.ts +0 -3
- package/dist/reown/providers/index.js +0 -3
- package/dist/reown/utils/account.d.ts +0 -2
- package/dist/reown/utils/account.js +0 -20
- package/dist/reown/utils/chains.d.ts +0 -18
- package/dist/reown/utils/chains.js +0 -152
- package/dist/reown/utils/constants.d.ts +0 -16
- package/dist/reown/utils/constants.js +0 -18
- package/dist/reown/utils/helpers.d.ts +0 -12
- package/dist/reown/utils/helpers.js +0 -25
- package/dist/reown/utils/index.d.ts +0 -5
- package/dist/reown/utils/index.js +0 -5
- package/dist/reown/utils/types.d.ts +0 -9
- package/dist/reown/utils/types.js +0 -1
- package/dist/reown/wallets/EIP155Wallet.d.ts +0 -46
- package/dist/reown/wallets/EIP155Wallet.js +0 -124
- package/dist/reown/wallets/HIP820Wallet.d.ts +0 -53
- package/dist/reown/wallets/HIP820Wallet.js +0 -236
- package/dist/reown/wallets/index.d.ts +0 -2
- package/dist/reown/wallets/index.js +0 -2
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ read transactions. Hedera implements EVM compatible smart contracts using
|
|
|
13
13
|
[Hyperledger Besu](https://besu.hyperledger.org/) under the hood.
|
|
14
14
|
|
|
15
15
|
Ethereum developers and toolsets often expect to interact with Ethereum compatible chains using
|
|
16
|
-
the [Ethereum JSON-RPC](https://ethereum.org/en/developers/docs/apis/json-rpc/). To
|
|
16
|
+
the [Ethereum JSON-RPC](https://ethereum.org/en/developers/docs/apis/json-rpc/). To achieve
|
|
17
17
|
compatibility with this API,
|
|
18
18
|
[Hedera JSON-RPC Providers](https://docs.hedera.com/hedera/core-concepts/smart-contracts/json-rpc-relay#community-hosted-json-rpc-relays)
|
|
19
19
|
operate a software middlelayer that translates Ethereum JSON-RPC compatible API calls into
|
|
@@ -26,7 +26,7 @@ transactions to wallets over the WalletConnect network using the JSON-RPC spec d
|
|
|
26
26
|
Hedera native transactions or use Ethereum JSON-RPC calls sent to a Hedera JSON-RPC Relay
|
|
27
27
|
provider which then communicates with Hedera consensus and mirror nodes.
|
|
28
28
|
|
|
29
|
-
On a high level, JSON-RPC is a type of API
|
|
29
|
+
On a high level, JSON-RPC is a type of API structure, such as SOAP, gRPC, REST, GraphQL, etc. In
|
|
30
30
|
the Hedera ecosystem, there are distinct concepts regarding JSON-RPC APIs to consider:
|
|
31
31
|
|
|
32
32
|
- Ethereum JSON-RPC spec defines how to interact with Ethereum compatible networks
|
|
@@ -34,7 +34,7 @@ the Hedera ecosystem, there are distinct concepts regarding JSON-RPC APIs to con
|
|
|
34
34
|
- Wallets in the Hedera ecosystem also support a separate specification that defines how to send
|
|
35
35
|
transactions and messages to wallets over the WalletConnect network without relying on a
|
|
36
36
|
Hedera JSON-RPC Relay provider. This is a Hedera specific specification defined for utilizing
|
|
37
|
-
the WalletConnect network
|
|
37
|
+
the WalletConnect network distinct from other JSON-RPC specs such as the one defined by the
|
|
38
38
|
Ethereum network.
|
|
39
39
|
|
|
40
40
|
For more information see:
|
|
@@ -64,7 +64,7 @@ reviewing the [Reown docs](https://docs.reown.com/overview).
|
|
|
64
64
|
1. Add Hedera dependencies to your project:
|
|
65
65
|
|
|
66
66
|
```sh
|
|
67
|
-
npm install @hashgraph/hedera-wallet-connect
|
|
67
|
+
npm install @hashgraph/hedera-wallet-connect @hashgraph/sdk @walletconnect/modal
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
2. Initialize dApp Connector
|
|
@@ -121,7 +121,7 @@ await dAppConnector.openModal()
|
|
|
121
121
|
2. Add Hedera dependencies to your project:
|
|
122
122
|
|
|
123
123
|
```sh
|
|
124
|
-
npm install @hashgraph/hedera-wallet-connect
|
|
124
|
+
npm install @hashgraph/hedera-wallet-connect @hashgraph/sdk @walletconnect/universal-provider
|
|
125
125
|
```
|
|
126
126
|
|
|
127
127
|
3. Update `createAppKit` with adapters and a universal provider for Hedera. Note the
|
|
@@ -154,7 +154,7 @@ const hederaEVMAdapter = new HederaAdapter({
|
|
|
154
154
|
})
|
|
155
155
|
|
|
156
156
|
const universalProvider = (await HederaProvider.init({
|
|
157
|
-
projectId: "YOUR_PROJECT_ID"
|
|
157
|
+
projectId: "YOUR_PROJECT_ID",
|
|
158
158
|
metadata,
|
|
159
159
|
})) as unknown as UniversalProvider, // avoid type mismatch error due to missing of private properties in HederaProvider
|
|
160
160
|
|
|
@@ -211,6 +211,141 @@ createAppKit({
|
|
|
211
211
|
- [Hedera Wallet Example by Hgraph](https://github.com/hgraph-io/hedera-wallet)
|
|
212
212
|
- <em>[Add an example, demo, or tool here](https://github.com/hashgraph/hedera-wallet-connect/pulls)</em>
|
|
213
213
|
|
|
214
|
+
# Multi-Signature Transactions
|
|
215
|
+
|
|
216
|
+
Multi-signature (multi-sig) workflows allow multiple parties to sign a single transaction before it's executed on the Hedera network. This is commonly used for:
|
|
217
|
+
|
|
218
|
+
- Treasury operations requiring approval from multiple parties
|
|
219
|
+
- Escrow services
|
|
220
|
+
- Joint accounts
|
|
221
|
+
- Backend co-signing for additional security
|
|
222
|
+
|
|
223
|
+
## Using `hedera_signTransaction` for Multi-Sig Workflows
|
|
224
|
+
|
|
225
|
+
The `hedera_signTransaction` method allows you to collect a signature from a wallet without immediately executing the transaction. This signature can then be combined with additional signatures (such as from a backend service) before final execution.
|
|
226
|
+
|
|
227
|
+
### Example: Frontend Wallet Signature + Backend Co-Signature
|
|
228
|
+
|
|
229
|
+
This example demonstrates a common pattern where a user signs a transaction in their wallet, and then a backend service adds its signature before executing the transaction.
|
|
230
|
+
|
|
231
|
+
#### Step 1: Create and Sign Transaction on Frontend
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import { DAppConnector, HederaJsonRpcMethod } from '@hashgraph/hedera-wallet-connect'
|
|
235
|
+
import { TransferTransaction, Hbar, AccountId } from '@hashgraph/sdk'
|
|
236
|
+
|
|
237
|
+
// Initialize your DAppConnector (see Getting Started section)
|
|
238
|
+
const dAppConnector = new DAppConnector(/* ... */)
|
|
239
|
+
|
|
240
|
+
// Create a transaction
|
|
241
|
+
const transaction = new TransferTransaction()
|
|
242
|
+
.addHbarTransfer(userAccountId, new Hbar(-10))
|
|
243
|
+
.addHbarTransfer(recipientAccountId, new Hbar(10))
|
|
244
|
+
.setTransactionMemo('Multi-sig transfer')
|
|
245
|
+
|
|
246
|
+
// Request signature from wallet (does NOT execute)
|
|
247
|
+
const signer = dAppConnector.getSigner(userAccountId)
|
|
248
|
+
const signedTransaction = await signer.signTransaction(transaction)
|
|
249
|
+
|
|
250
|
+
// Convert signed transaction to bytes for transmission to backend
|
|
251
|
+
const signedTransactionBytes = signedTransaction.toBytes()
|
|
252
|
+
|
|
253
|
+
// Send to backend
|
|
254
|
+
const response = await fetch('/api/execute-transaction', {
|
|
255
|
+
method: 'POST',
|
|
256
|
+
headers: { 'Content-Type': 'application/json' },
|
|
257
|
+
body: JSON.stringify({
|
|
258
|
+
signedTransaction: Buffer.from(signedTransactionBytes).toString('base64'),
|
|
259
|
+
}),
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
const result = await response.json()
|
|
263
|
+
console.log('Transaction executed:', result.transactionId)
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
#### Step 2: Add Backend Signature and Execute
|
|
267
|
+
|
|
268
|
+
On your backend, use the `addSignatureToTransaction` utility to add your server's signature:
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
import { Transaction, PrivateKey, Client } from '@hashgraph/sdk'
|
|
272
|
+
import { addSignatureToTransaction } from '@hashgraph/hedera-wallet-connect'
|
|
273
|
+
|
|
274
|
+
// Backend API endpoint
|
|
275
|
+
app.post('/api/execute-transaction', async (req, res) => {
|
|
276
|
+
try {
|
|
277
|
+
// Reconstruct transaction from bytes
|
|
278
|
+
const signedTransactionBytes = Buffer.from(req.body.signedTransaction, 'base64')
|
|
279
|
+
const signedTransaction = Transaction.fromBytes(signedTransactionBytes)
|
|
280
|
+
|
|
281
|
+
// Load your backend private key (store securely!)
|
|
282
|
+
const backendPrivateKey = PrivateKey.fromStringED25519(process.env.BACKEND_PRIVATE_KEY)
|
|
283
|
+
|
|
284
|
+
// Add backend signature to the transaction
|
|
285
|
+
const fullySignedTransaction = await addSignatureToTransaction(
|
|
286
|
+
signedTransaction,
|
|
287
|
+
backendPrivateKey,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
// Execute the fully signed transaction
|
|
291
|
+
const client = Client.forTestnet() // or Client.forMainnet()
|
|
292
|
+
client.setOperator(backendAccountId, backendPrivateKey)
|
|
293
|
+
|
|
294
|
+
const txResponse = await fullySignedTransaction.execute(client)
|
|
295
|
+
const receipt = await txResponse.getReceipt(client)
|
|
296
|
+
|
|
297
|
+
res.json({
|
|
298
|
+
success: true,
|
|
299
|
+
transactionId: txResponse.transactionId.toString(),
|
|
300
|
+
status: receipt.status.toString(),
|
|
301
|
+
})
|
|
302
|
+
} catch (error) {
|
|
303
|
+
console.error('Error executing transaction:', error)
|
|
304
|
+
res.status(500).json({ error: error.message })
|
|
305
|
+
}
|
|
306
|
+
})
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Important Notes
|
|
310
|
+
|
|
311
|
+
1. **Transaction Must Be Frozen**: Before signing, ensure your transaction is frozen.
|
|
312
|
+
|
|
313
|
+
2. **Signature Order**: Signatures can be added in any order. Hedera validates that all required signatures are present when the transaction is executed.
|
|
314
|
+
|
|
315
|
+
3. **Security Considerations**:
|
|
316
|
+
- Never expose backend private keys to the frontend
|
|
317
|
+
- Validate transaction contents on the backend before adding your signature
|
|
318
|
+
- Implement proper authentication and authorization
|
|
319
|
+
- Consider implementing transaction limits and approval workflows
|
|
320
|
+
|
|
321
|
+
4. **Multiple Signatures**: You can add more than two signatures using the same pattern:
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
// Add multiple signatures sequentially
|
|
325
|
+
let signedTx = await addSignatureToTransaction(transaction, privateKey1)
|
|
326
|
+
signedTx = await addSignatureToTransaction(signedTx, privateKey2)
|
|
327
|
+
signedTx = await addSignatureToTransaction(signedTx, privateKey3)
|
|
328
|
+
|
|
329
|
+
// Execute with all signatures
|
|
330
|
+
await signedTx.execute(client)
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
5. **Threshold Keys**: For accounts with threshold key structures, ensure you collect enough signatures to meet the threshold requirement before execution.
|
|
334
|
+
|
|
335
|
+
### Alternative: Using `hedera_signAndExecuteTransaction`
|
|
336
|
+
|
|
337
|
+
If you don't need backend co-signing and want the wallet to execute the transaction immediately:
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
// This signs AND executes in one call
|
|
341
|
+
const result = await dAppConnector.signAndExecuteTransaction({
|
|
342
|
+
signerAccountId: `hedera:testnet:${userAccountId}`,
|
|
343
|
+
transactionList: transactionToBase64String(transaction),
|
|
344
|
+
})
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
Use `hedera_signTransaction` when you need to collect multiple signatures. Use `hedera_signAndExecuteTransaction` when the wallet's signature alone is sufficient to execute the transaction.
|
|
348
|
+
|
|
214
349
|
# Hedera Wallets
|
|
215
350
|
|
|
216
351
|
- [Hashpack](https://hashpack.app/)
|
|
@@ -226,12 +361,10 @@ refer to how to send transactions to wallets using the `hedera:(mainnet|testnet)
|
|
|
226
361
|
While minimal, the main breaking changes are:
|
|
227
362
|
|
|
228
363
|
- remove WalletConnect v1 modals
|
|
229
|
-
|
|
230
364
|
- these are very old, though in the spirit of semver, we kept the dependency until this
|
|
231
365
|
library's v2 release
|
|
232
366
|
|
|
233
367
|
- remove setting node id's within this library for transactions
|
|
234
|
-
|
|
235
368
|
- initially, a transaction created by the Hedera Javascript SDK needed to have one or more
|
|
236
369
|
consensus node ids set to be able to serialize into bytes, sent over a network, and
|
|
237
370
|
deserialized by the SDK
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hashgraph/hedera-wallet-connect",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5-canary.09073be.0",
|
|
4
4
|
"description": "A library to facilitate integrating Hedera with WalletConnect",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"@hashgraph/sdk": "^2.63.0",
|
|
32
|
-
"@reown/appkit": "
|
|
32
|
+
"@reown/appkit": "1.8.10",
|
|
33
|
+
"@reown/appkit-controllers": "1.8.10",
|
|
33
34
|
"@reown/walletkit": "^1.2.8",
|
|
34
35
|
"@walletconnect/modal": "^2.7.0",
|
|
35
36
|
"ethers": "^6.13.5"
|
|
@@ -38,6 +39,7 @@
|
|
|
38
39
|
"@swc/jest": "^0.2.37",
|
|
39
40
|
"@types/jest": "^30.0.0",
|
|
40
41
|
"jest": "^30.0.3",
|
|
42
|
+
"nodemon": "^3.1.10",
|
|
41
43
|
"prettier": "^3.5.3",
|
|
42
44
|
"ts-node": "^10.9.2",
|
|
43
45
|
"typescript": "^5.8.2"
|
package/dist/index.d.ts
DELETED
package/dist/index.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
*
|
|
3
|
-
* Hedera Wallet Connect
|
|
4
|
-
*
|
|
5
|
-
* Copyright (C) 2023 Hedera Hashgraph, LLC
|
|
6
|
-
*
|
|
7
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
-
* you may not use this file except in compliance with the License.
|
|
9
|
-
* You may obtain a copy of the License at
|
|
10
|
-
*
|
|
11
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
-
*
|
|
13
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
-
* See the License for the specific language governing permissions and
|
|
17
|
-
* limitations under the License.
|
|
18
|
-
*
|
|
19
|
-
*/
|
|
20
|
-
export * from './lib';
|
|
21
|
-
export * from './reown';
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { Signer, AccountBalance, AccountId, AccountInfo, Executable, Key, LedgerId, SignerSignature, Transaction, TransactionRecord } from '@hashgraph/sdk';
|
|
2
|
-
import type { CoreTypes, ISignClient } from '@walletconnect/types';
|
|
3
|
-
import { LogLevel } from '../shared/logger';
|
|
4
|
-
export declare class DAppSigner implements Signer {
|
|
5
|
-
private readonly accountId;
|
|
6
|
-
private readonly signClient;
|
|
7
|
-
readonly topic: string;
|
|
8
|
-
private readonly ledgerId;
|
|
9
|
-
readonly extensionId?: string | undefined;
|
|
10
|
-
private logger;
|
|
11
|
-
constructor(accountId: AccountId, signClient: ISignClient, topic: string, ledgerId?: LedgerId, extensionId?: string | undefined, logLevel?: LogLevel);
|
|
12
|
-
/**
|
|
13
|
-
* Sets the logging level for the DAppSigner
|
|
14
|
-
* @param level - The logging level to set
|
|
15
|
-
*/
|
|
16
|
-
setLogLevel(level: LogLevel): void;
|
|
17
|
-
private _getHederaClient;
|
|
18
|
-
private get _signerAccountId();
|
|
19
|
-
request<T>(request: {
|
|
20
|
-
method: string;
|
|
21
|
-
params: any;
|
|
22
|
-
}): Promise<T>;
|
|
23
|
-
getAccountId(): AccountId;
|
|
24
|
-
getAccountKey(): Key;
|
|
25
|
-
getLedgerId(): LedgerId;
|
|
26
|
-
getNetwork(): {
|
|
27
|
-
[key: string]: string | AccountId;
|
|
28
|
-
};
|
|
29
|
-
getMirrorNetwork(): string[];
|
|
30
|
-
getAccountBalance(): Promise<AccountBalance>;
|
|
31
|
-
getAccountInfo(): Promise<AccountInfo>;
|
|
32
|
-
getAccountRecords(): Promise<TransactionRecord[]>;
|
|
33
|
-
getMetadata(): CoreTypes.Metadata;
|
|
34
|
-
sign(data: Uint8Array[], signOptions?: {
|
|
35
|
-
encoding?: 'utf-8' | 'base64';
|
|
36
|
-
}): Promise<SignerSignature[]>;
|
|
37
|
-
checkTransaction<T extends Transaction>(transaction: T): Promise<T>;
|
|
38
|
-
populateTransaction<T extends Transaction>(transaction: T): Promise<T>;
|
|
39
|
-
/**
|
|
40
|
-
* Prepares a transaction object for signing using a single node account id.
|
|
41
|
-
* If the transaction object does not already have a node account id,
|
|
42
|
-
* generate a random node account id using the Hedera SDK client
|
|
43
|
-
*
|
|
44
|
-
* @param transaction - Any instance of a class that extends `Transaction`
|
|
45
|
-
* @returns transaction - `Transaction` object with signature
|
|
46
|
-
*/
|
|
47
|
-
signTransaction<T extends Transaction>(transaction: T): Promise<T>;
|
|
48
|
-
private _tryExecuteTransactionRequest;
|
|
49
|
-
private _parseQueryResponse;
|
|
50
|
-
/**
|
|
51
|
-
* Executes a free receipt query without signing a transaction.
|
|
52
|
-
* Enables the DApp to fetch the receipt of a transaction without making a new request
|
|
53
|
-
* to the wallet.
|
|
54
|
-
* @param request - The query to execute
|
|
55
|
-
* @returns The result of the query
|
|
56
|
-
*/
|
|
57
|
-
private executeReceiptQueryFromRequest;
|
|
58
|
-
private _tryExecuteQueryRequest;
|
|
59
|
-
call<RequestT, ResponseT, OutputT>(request: Executable<RequestT, ResponseT, OutputT>): Promise<OutputT>;
|
|
60
|
-
}
|
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
*
|
|
3
|
-
* Hedera Wallet Connect
|
|
4
|
-
*
|
|
5
|
-
* Copyright (C) 2023 Hedera Hashgraph, LLC
|
|
6
|
-
*
|
|
7
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
-
* you may not use this file except in compliance with the License.
|
|
9
|
-
* You may obtain a copy of the License at
|
|
10
|
-
*
|
|
11
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
-
*
|
|
13
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
-
* See the License for the specific language governing permissions and
|
|
17
|
-
* limitations under the License.
|
|
18
|
-
*
|
|
19
|
-
*/
|
|
20
|
-
import { AccountBalance, AccountInfo, LedgerId, SignerSignature, Transaction, TransactionRecord, Client, PublicKey, TransactionId, TransactionResponse, Query, AccountRecordsQuery, AccountInfoQuery, AccountBalanceQuery, TransactionReceiptQuery, TransactionReceipt, TransactionRecordQuery, } from '@hashgraph/sdk';
|
|
21
|
-
import { proto } from '@hashgraph/proto';
|
|
22
|
-
import { HederaJsonRpcMethod, base64StringToSignatureMap, base64StringToUint8Array, ledgerIdToCAIPChainId, queryToBase64String, transactionBodyToBase64String, transactionToBase64String, transactionToTransactionBody, extensionOpen, Uint8ArrayToBase64String, Uint8ArrayToString, } from '../shared';
|
|
23
|
-
import { DefaultLogger } from '../shared/logger';
|
|
24
|
-
import { SessionNotFoundError } from './SessionNotFoundError';
|
|
25
|
-
const clients = {};
|
|
26
|
-
export class DAppSigner {
|
|
27
|
-
constructor(accountId, signClient, topic, ledgerId = LedgerId.MAINNET, extensionId, logLevel = 'debug') {
|
|
28
|
-
this.accountId = accountId;
|
|
29
|
-
this.signClient = signClient;
|
|
30
|
-
this.topic = topic;
|
|
31
|
-
this.ledgerId = ledgerId;
|
|
32
|
-
this.extensionId = extensionId;
|
|
33
|
-
this.logger = new DefaultLogger(logLevel);
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Sets the logging level for the DAppSigner
|
|
37
|
-
* @param level - The logging level to set
|
|
38
|
-
*/
|
|
39
|
-
setLogLevel(level) {
|
|
40
|
-
if (this.logger instanceof DefaultLogger) {
|
|
41
|
-
this.logger.setLogLevel(level);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
_getHederaClient() {
|
|
45
|
-
const ledgerIdString = this.ledgerId.toString();
|
|
46
|
-
if (!clients[ledgerIdString]) {
|
|
47
|
-
clients[ledgerIdString] = Client.forName(ledgerIdString);
|
|
48
|
-
}
|
|
49
|
-
return clients[ledgerIdString];
|
|
50
|
-
}
|
|
51
|
-
get _signerAccountId() {
|
|
52
|
-
return `${ledgerIdToCAIPChainId(this.ledgerId)}:${this.accountId.toString()}`;
|
|
53
|
-
}
|
|
54
|
-
request(request) {
|
|
55
|
-
var _a, _b;
|
|
56
|
-
// Avoid a wallet call if the session is no longer valid
|
|
57
|
-
if (!((_b = (_a = this === null || this === void 0 ? void 0 : this.signClient) === null || _a === void 0 ? void 0 : _a.session) === null || _b === void 0 ? void 0 : _b.get(this.topic))) {
|
|
58
|
-
this.logger.error('Session no longer exists, signer will be removed. Please reconnect to the wallet.');
|
|
59
|
-
// Notify DAppConnector to remove this signer
|
|
60
|
-
this.signClient.emit({
|
|
61
|
-
topic: this.topic,
|
|
62
|
-
event: {
|
|
63
|
-
name: 'session_delete',
|
|
64
|
-
data: { topic: this.topic },
|
|
65
|
-
},
|
|
66
|
-
chainId: ledgerIdToCAIPChainId(this.ledgerId),
|
|
67
|
-
});
|
|
68
|
-
throw new SessionNotFoundError('Session no longer exists. Please reconnect to the wallet.');
|
|
69
|
-
}
|
|
70
|
-
if (this.extensionId)
|
|
71
|
-
extensionOpen(this.extensionId);
|
|
72
|
-
return this.signClient.request({
|
|
73
|
-
topic: this.topic,
|
|
74
|
-
request,
|
|
75
|
-
chainId: ledgerIdToCAIPChainId(this.ledgerId),
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
getAccountId() {
|
|
79
|
-
return this.accountId;
|
|
80
|
-
}
|
|
81
|
-
getAccountKey() {
|
|
82
|
-
throw new Error('Method not implemented.');
|
|
83
|
-
}
|
|
84
|
-
getLedgerId() {
|
|
85
|
-
return this.ledgerId;
|
|
86
|
-
}
|
|
87
|
-
getNetwork() {
|
|
88
|
-
return this._getHederaClient().network;
|
|
89
|
-
}
|
|
90
|
-
getMirrorNetwork() {
|
|
91
|
-
return this._getHederaClient().mirrorNetwork;
|
|
92
|
-
}
|
|
93
|
-
getAccountBalance() {
|
|
94
|
-
return this.call(new AccountBalanceQuery().setAccountId(this.accountId));
|
|
95
|
-
}
|
|
96
|
-
getAccountInfo() {
|
|
97
|
-
return this.call(new AccountInfoQuery().setAccountId(this.accountId));
|
|
98
|
-
}
|
|
99
|
-
getAccountRecords() {
|
|
100
|
-
return this.call(new AccountRecordsQuery().setAccountId(this.accountId));
|
|
101
|
-
}
|
|
102
|
-
getMetadata() {
|
|
103
|
-
return this.signClient.metadata;
|
|
104
|
-
}
|
|
105
|
-
async sign(data, signOptions = {
|
|
106
|
-
encoding: 'utf-8',
|
|
107
|
-
}) {
|
|
108
|
-
try {
|
|
109
|
-
const messageToSign = signOptions.encoding === 'base64'
|
|
110
|
-
? Uint8ArrayToBase64String(data[0])
|
|
111
|
-
: Uint8ArrayToString(data[0]);
|
|
112
|
-
const { signatureMap } = await this.request({
|
|
113
|
-
method: HederaJsonRpcMethod.SignMessage,
|
|
114
|
-
params: {
|
|
115
|
-
signerAccountId: this._signerAccountId,
|
|
116
|
-
message: messageToSign,
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
const sigmap = base64StringToSignatureMap(signatureMap);
|
|
120
|
-
const signerSignature = new SignerSignature({
|
|
121
|
-
accountId: this.getAccountId(),
|
|
122
|
-
publicKey: PublicKey.fromBytes(sigmap.sigPair[0].pubKeyPrefix),
|
|
123
|
-
signature: sigmap.sigPair[0].ed25519 ||
|
|
124
|
-
sigmap.sigPair[0].ECDSASecp256k1,
|
|
125
|
-
});
|
|
126
|
-
this.logger.debug('Data signed successfully');
|
|
127
|
-
return [signerSignature];
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
this.logger.error('Error signing data:', error);
|
|
131
|
-
throw error;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
async checkTransaction(transaction) {
|
|
135
|
-
throw new Error('Method not implemented.');
|
|
136
|
-
}
|
|
137
|
-
async populateTransaction(transaction) {
|
|
138
|
-
return transaction.setTransactionId(TransactionId.generate(this.getAccountId()));
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Prepares a transaction object for signing using a single node account id.
|
|
142
|
-
* If the transaction object does not already have a node account id,
|
|
143
|
-
* generate a random node account id using the Hedera SDK client
|
|
144
|
-
*
|
|
145
|
-
* @param transaction - Any instance of a class that extends `Transaction`
|
|
146
|
-
* @returns transaction - `Transaction` object with signature
|
|
147
|
-
*/
|
|
148
|
-
async signTransaction(transaction) {
|
|
149
|
-
const transactionBody = transactionToTransactionBody(transaction);
|
|
150
|
-
if (!transactionBody)
|
|
151
|
-
throw new Error('Failed to serialize transaction body');
|
|
152
|
-
const transactionBodyBase64 = transactionBodyToBase64String(transactionBody);
|
|
153
|
-
const { signatureMap } = await this.request({
|
|
154
|
-
method: HederaJsonRpcMethod.SignTransaction,
|
|
155
|
-
params: {
|
|
156
|
-
signerAccountId: this._signerAccountId,
|
|
157
|
-
transactionBody: transactionBodyBase64,
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
const sigMap = base64StringToSignatureMap(signatureMap);
|
|
161
|
-
const bodyBytes = base64StringToUint8Array(transactionBodyBase64);
|
|
162
|
-
const bytes = proto.Transaction.encode({ bodyBytes, sigMap }).finish();
|
|
163
|
-
return Transaction.fromBytes(bytes);
|
|
164
|
-
}
|
|
165
|
-
async _tryExecuteTransactionRequest(request) {
|
|
166
|
-
try {
|
|
167
|
-
const requestToBytes = request.toBytes();
|
|
168
|
-
this.logger.debug('Creating transaction from bytes', requestToBytes, request);
|
|
169
|
-
const transaction = Transaction.fromBytes(requestToBytes);
|
|
170
|
-
this.logger.debug('Executing transaction request', transaction);
|
|
171
|
-
const result = await this.request({
|
|
172
|
-
method: HederaJsonRpcMethod.SignAndExecuteTransaction,
|
|
173
|
-
params: {
|
|
174
|
-
signerAccountId: this._signerAccountId,
|
|
175
|
-
transactionList: transactionToBase64String(transaction),
|
|
176
|
-
},
|
|
177
|
-
});
|
|
178
|
-
this.logger.debug('Transaction request completed successfully');
|
|
179
|
-
return { result: TransactionResponse.fromJSON(result) };
|
|
180
|
-
}
|
|
181
|
-
catch (error) {
|
|
182
|
-
this.logger.error('Error executing transaction request:', error);
|
|
183
|
-
return { error };
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
async _parseQueryResponse(query, base64EncodedQueryResponse) {
|
|
187
|
-
if (query instanceof AccountRecordsQuery) {
|
|
188
|
-
const base64EncodedQueryResponseSplit = base64EncodedQueryResponse.split(',');
|
|
189
|
-
const data = base64EncodedQueryResponseSplit.map((o) => base64StringToUint8Array(o));
|
|
190
|
-
return data.map((o) => TransactionRecord.fromBytes(o));
|
|
191
|
-
}
|
|
192
|
-
const data = base64StringToUint8Array(base64EncodedQueryResponse);
|
|
193
|
-
if (query instanceof AccountBalanceQuery) {
|
|
194
|
-
return AccountBalance.fromBytes(data);
|
|
195
|
-
}
|
|
196
|
-
else if (query instanceof AccountInfoQuery) {
|
|
197
|
-
return AccountInfo.fromBytes(data);
|
|
198
|
-
}
|
|
199
|
-
else if (query instanceof TransactionReceiptQuery) {
|
|
200
|
-
return TransactionReceipt.fromBytes(data);
|
|
201
|
-
}
|
|
202
|
-
else if (query instanceof TransactionRecordQuery) {
|
|
203
|
-
return TransactionRecord.fromBytes(data);
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
throw new Error('Unsupported query type');
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Executes a free receipt query without signing a transaction.
|
|
211
|
-
* Enables the DApp to fetch the receipt of a transaction without making a new request
|
|
212
|
-
* to the wallet.
|
|
213
|
-
* @param request - The query to execute
|
|
214
|
-
* @returns The result of the query
|
|
215
|
-
*/
|
|
216
|
-
async executeReceiptQueryFromRequest(request) {
|
|
217
|
-
try {
|
|
218
|
-
const isMainnet = this.ledgerId === LedgerId.MAINNET;
|
|
219
|
-
const client = isMainnet ? Client.forMainnet() : Client.forTestnet();
|
|
220
|
-
const receipt = TransactionReceiptQuery.fromBytes(request.toBytes());
|
|
221
|
-
const result = await receipt.execute(client);
|
|
222
|
-
return { result };
|
|
223
|
-
}
|
|
224
|
-
catch (error) {
|
|
225
|
-
return { error };
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
async _tryExecuteQueryRequest(request) {
|
|
229
|
-
try {
|
|
230
|
-
const isReceiptQuery = request instanceof TransactionReceiptQuery;
|
|
231
|
-
if (isReceiptQuery) {
|
|
232
|
-
this.logger.debug('Attempting to execute free receipt query', request);
|
|
233
|
-
const result = await this.executeReceiptQueryFromRequest(request);
|
|
234
|
-
if (!(result === null || result === void 0 ? void 0 : result.error)) {
|
|
235
|
-
return { result: result.result };
|
|
236
|
-
}
|
|
237
|
-
this.logger.error('Error executing free receipt query. Sending to wallet.', result.error);
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Note, should we be converting these to specific query types?
|
|
241
|
-
* Left alone to avoid changing the API for other requests.
|
|
242
|
-
*/
|
|
243
|
-
const query = isReceiptQuery
|
|
244
|
-
? TransactionReceiptQuery.fromBytes(request.toBytes())
|
|
245
|
-
: Query.fromBytes(request.toBytes());
|
|
246
|
-
this.logger.debug('Executing query request', query, queryToBase64String(query), isReceiptQuery);
|
|
247
|
-
const result = await this.request({
|
|
248
|
-
method: HederaJsonRpcMethod.SignAndExecuteQuery,
|
|
249
|
-
params: {
|
|
250
|
-
signerAccountId: this._signerAccountId,
|
|
251
|
-
query: queryToBase64String(query),
|
|
252
|
-
},
|
|
253
|
-
});
|
|
254
|
-
this.logger.debug('Query request completed successfully', result);
|
|
255
|
-
return { result: this._parseQueryResponse(query, result.response) };
|
|
256
|
-
}
|
|
257
|
-
catch (error) {
|
|
258
|
-
this.logger.error('Error executing query request:', error);
|
|
259
|
-
return { error };
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
async call(request) {
|
|
263
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
264
|
-
const isReceiptQuery = request instanceof TransactionReceiptQuery;
|
|
265
|
-
let txResult = undefined;
|
|
266
|
-
// a receipt query is a free query and we should not execute a transaction.
|
|
267
|
-
if (!isReceiptQuery) {
|
|
268
|
-
txResult = await this._tryExecuteTransactionRequest(request);
|
|
269
|
-
if (txResult.result) {
|
|
270
|
-
return txResult.result;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
const queryResult = await this._tryExecuteQueryRequest(request);
|
|
274
|
-
if (queryResult.result) {
|
|
275
|
-
return queryResult.result;
|
|
276
|
-
}
|
|
277
|
-
if (isReceiptQuery) {
|
|
278
|
-
throw new Error('Error executing receipt query: \n' +
|
|
279
|
-
JSON.stringify({
|
|
280
|
-
queryError: {
|
|
281
|
-
name: (_a = queryResult.error) === null || _a === void 0 ? void 0 : _a.name,
|
|
282
|
-
message: (_b = queryResult.error) === null || _b === void 0 ? void 0 : _b.message,
|
|
283
|
-
stack: (_c = queryResult.error) === null || _c === void 0 ? void 0 : _c.stack,
|
|
284
|
-
},
|
|
285
|
-
}));
|
|
286
|
-
}
|
|
287
|
-
throw new Error('Error executing transaction or query: \n' +
|
|
288
|
-
JSON.stringify({
|
|
289
|
-
txError: {
|
|
290
|
-
name: (_d = txResult === null || txResult === void 0 ? void 0 : txResult.error) === null || _d === void 0 ? void 0 : _d.name,
|
|
291
|
-
message: (_e = txResult === null || txResult === void 0 ? void 0 : txResult.error) === null || _e === void 0 ? void 0 : _e.message,
|
|
292
|
-
stack: (_f = txResult === null || txResult === void 0 ? void 0 : txResult.error) === null || _f === void 0 ? void 0 : _f.stack,
|
|
293
|
-
},
|
|
294
|
-
queryError: {
|
|
295
|
-
name: (_g = queryResult.error) === null || _g === void 0 ? void 0 : _g.name,
|
|
296
|
-
message: (_h = queryResult.error) === null || _h === void 0 ? void 0 : _h.message,
|
|
297
|
-
stack: (_j = queryResult.error) === null || _j === void 0 ? void 0 : _j.stack,
|
|
298
|
-
},
|
|
299
|
-
}, null, 2));
|
|
300
|
-
}
|
|
301
|
-
}
|