-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Transactions Module): sendTransaction & waitForTransaction (#322)
* feat: transactions module in thor client * docs: refactor thor client tsdocs * feat: transactions module with sendTx & waitForTx * feat: tx module types * feat: tx module assertions helper * test: transactions module * chore: indexing & minors * fix: miss some export --------- Co-authored-by: rodolfopietro97 <[email protected]>
- Loading branch information
1 parent
e2ae986
commit cb161b9
Showing
10 changed files
with
373 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
// Single clients | ||
export * from './nodes'; | ||
export * from './transactions'; | ||
|
||
// Main client | ||
export * from './thor-client'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
packages/network/src/clients/thor-client/transactions/helpers/assertions.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { type Transaction } from '@vechainfoundation/vechain-sdk-core'; | ||
import { TRANSACTION, assert } from '@vechainfoundation/vechain-sdk-errors'; | ||
|
||
/** | ||
* Asserts that the given transaction is signed. | ||
* @param tx - The transaction to check. | ||
* | ||
* @throws {InvalidTransactionError} if the transaction is not signed. | ||
*/ | ||
const assertIsSignedTx = (tx: Transaction): void => { | ||
assert(tx.isSigned, TRANSACTION.NOT_SIGNED, 'Transaction must be signed.', { | ||
tx | ||
}); | ||
}; | ||
|
||
export { assertIsSignedTx }; |
1 change: 1 addition & 0 deletions
1
packages/network/src/clients/thor-client/transactions/helpers/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './assertions'; |
2 changes: 2 additions & 0 deletions
2
packages/network/src/clients/thor-client/transactions/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './types.d'; | ||
export * from './transactions-module'; |
76 changes: 76 additions & 0 deletions
76
packages/network/src/clients/thor-client/transactions/transactions-module.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import { type Transaction } from '@vechainfoundation/vechain-sdk-core'; | ||
import { Poll, type HttpClient } from '../../../utils'; | ||
import { | ||
type TransactionReceipt, | ||
TransactionsClient | ||
} from '../../thorest-client'; | ||
import { type WaitForTransactionOptions } from './types'; | ||
import { assertIsSignedTx } from './helpers'; | ||
|
||
/** | ||
* The `TransactionsModule` handles transaction related operations and provides | ||
* convenient methods for sending transactions and waiting for transaction confirmation. | ||
*/ | ||
class TransactionsModule { | ||
/** | ||
* Reference to the `TransactionsClient` instance. | ||
*/ | ||
private readonly transactionsClient: TransactionsClient; | ||
|
||
/** | ||
* Initializes a new instance of the `TransactionsModule` class. | ||
* @param httpClient - The HTTP client instance used for making HTTP requests. | ||
*/ | ||
constructor(readonly httpClient: HttpClient) { | ||
this.transactionsClient = new TransactionsClient(httpClient); | ||
} | ||
|
||
/** | ||
* Sends a signed transaction to the network. | ||
* | ||
* @param signedTx - the transaction to send. It must be signed. | ||
* | ||
* @returns A promise that resolves to the transaction ID of the sent transaction. | ||
* | ||
* @throws an error if the transaction is not signed. | ||
*/ | ||
public async sendTransaction(signedTx: Transaction): Promise<string> { | ||
assertIsSignedTx(signedTx); | ||
|
||
const rawTx = `0x${signedTx.encoded.toString('hex')}`; | ||
|
||
const txID = (await this.transactionsClient.sendTransaction(rawTx)).id; | ||
|
||
return txID; | ||
} | ||
|
||
/** | ||
* Waits for a transaction to be included in a block. | ||
* | ||
* @param txID - The transaction ID of the transaction to wait for. | ||
* @param options - Optional parameters for the request. Includes the timeout and interval between requests. | ||
* Both parameters are in milliseconds. If the timeout is not specified, the request will not timeout! | ||
* | ||
* @returns A promise that resolves to the transaction receipt of the transaction. If the transaction is not included in a block before the timeout, | ||
* the promise will resolve to `null`. | ||
*/ | ||
public async waitForTransaction( | ||
txID: string, | ||
options?: WaitForTransactionOptions | ||
): Promise<TransactionReceipt | null> { | ||
const result = await Poll.SyncPoll( | ||
async () => | ||
await this.transactionsClient.getTransactionReceipt(txID), | ||
{ | ||
requestIntervalInMilliseconds: options?.intervalMs, | ||
maximumWaitingTimeInMilliseconds: options?.timeoutMs | ||
} | ||
).waitUntil((result) => { | ||
return result !== null; | ||
}); | ||
|
||
return result; | ||
} | ||
} | ||
|
||
export { TransactionsModule }; |
21 changes: 21 additions & 0 deletions
21
packages/network/src/clients/thor-client/transactions/types.d.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* --- Input options start --- */ | ||
|
||
/** | ||
* Options for `waitForTransaction` method. | ||
*/ | ||
interface WaitForTransactionOptions { | ||
/** | ||
* Timeout in milliseconds. | ||
* After this time, the method will throw an error. | ||
*/ | ||
timeoutMs?: number; | ||
/** | ||
* Interval in milliseconds. | ||
* The method will check the transaction status every `intervalMs` milliseconds. | ||
*/ | ||
intervalMs?: number; | ||
} | ||
|
||
/* --- Input options end --- */ | ||
|
||
export type { WaitForTransactionOptions }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
124 changes: 124 additions & 0 deletions
124
packages/network/tests/clients/thor-client/transactions/fixture.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import { | ||
type TransactionBody, | ||
TransactionUtils, | ||
contract, | ||
unitsUtils, | ||
networkInfo | ||
} from '@vechainfoundation/vechain-sdk-core'; | ||
import { BUILT_IN_CONTRACTS } from '../../../built-in-fixture'; | ||
import { TEST_ACCOUNTS } from '../../../fixture'; | ||
|
||
/** | ||
* Clause to transfer 1 VTHO to TEST_ACCOUNTS.TRANSACTION.TRANSACTION_RECEIVER | ||
*/ | ||
const transfer1VTHOClause = { | ||
to: BUILT_IN_CONTRACTS.ENERGY_ADDRESS, | ||
value: '0', | ||
data: contract.encodeFunctionInput( | ||
BUILT_IN_CONTRACTS.ENERGY_ABI, | ||
'transfer', | ||
[ | ||
TEST_ACCOUNTS.TRANSACTION.TRANSACTION_RECEIVER.address, | ||
unitsUtils.parseVET('1') | ||
] | ||
) | ||
}; | ||
|
||
/** | ||
* transaction body that transfers 1 VTHO to TEST_ACCOUNTS.TRANSACTION.TRANSACTION_RECEIVER | ||
*/ | ||
const transferTransactionBody: Omit<TransactionBody, 'nonce'> = { | ||
gas: 5000 + TransactionUtils.intrinsicGas([transfer1VTHOClause]) * 5, // @NOTE it is a temporary gas offered solution. This part will be replaced with estimateGas | ||
clauses: [transfer1VTHOClause], | ||
chainTag: networkInfo.solo.chainTag, | ||
blockRef: networkInfo.solo.genesisBlock.id.slice(0, 18), | ||
expiration: 1000, | ||
gasPriceCoef: 128, | ||
dependsOn: null | ||
}; | ||
|
||
/** | ||
* Expected transaction receipt values. | ||
* Note that this object is not a valid `TransactionReceipt` object. | ||
*/ | ||
const expectedReceipt = { | ||
events: [], | ||
gasPayer: '0x2669514f9fe96bc7301177ba774d3da8a06cace4', | ||
gasUsed: 36518, | ||
outputs: [ | ||
{ | ||
contractAddress: null, | ||
events: [ | ||
{ | ||
address: '0x0000000000000000000000000000456e65726779', | ||
data: '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000', | ||
topics: [ | ||
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', | ||
'0x0000000000000000000000002669514f9fe96bc7301177ba774d3da8a06cace4', | ||
'0x0000000000000000000000009e7911de289c3c856ce7f421034f66b6cde49c39' | ||
] | ||
} | ||
], | ||
transfers: [] | ||
} | ||
], | ||
reverted: false | ||
}; | ||
|
||
/** | ||
* waitForTransaction test cases that should return a transaction receipt | ||
*/ | ||
const waitForTransactionTestCases = [ | ||
{ | ||
description: | ||
'Should wait for transaction without timeout and return TransactionReceipt', | ||
options: { | ||
timeoutMs: undefined, | ||
intervalMs: undefined | ||
} | ||
}, | ||
{ | ||
description: | ||
'Should wait for transaction with timeout and return TransactionReceipt', | ||
options: { | ||
timeoutMs: 5000, | ||
intervalMs: undefined | ||
} | ||
}, | ||
{ | ||
description: | ||
'Should wait for transaction with intervalMs TransactionReceipt', | ||
options: { | ||
timeoutMs: undefined, | ||
intervalMs: 100 | ||
} | ||
}, | ||
{ | ||
description: | ||
'Should wait for transaction with intervalMs & timeoutMs and return TransactionReceipt', | ||
options: { | ||
timeoutMs: 5000, | ||
intervalMs: 100 | ||
} | ||
} | ||
]; | ||
|
||
/** | ||
* waitForTransaction test cases that should not return a transaction receipt. Instead, should return null. | ||
*/ | ||
const invalidWaitForTransactionTestCases = [ | ||
{ | ||
description: 'Should throw error when timeoutMs is too low', | ||
options: { | ||
timeoutMs: 1, | ||
intervalMs: undefined | ||
} | ||
} | ||
]; | ||
|
||
export { | ||
waitForTransactionTestCases, | ||
invalidWaitForTransactionTestCases, | ||
transferTransactionBody, | ||
expectedReceipt | ||
}; |
Oops, something went wrong.
cb161b9
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test Coverage
Summary