Skip to content

Commit

Permalink
Merge branch 'main' into add_transaction_example
Browse files Browse the repository at this point in the history
  • Loading branch information
0xmaayan authored Jan 24, 2024
2 parents 5b46e7e + 8221132 commit d114a05
Show file tree
Hide file tree
Showing 12 changed files with 248 additions and 32 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to the Aptos TypeScript SDK will be captured in this file. T

# Unreleased

- Remove request URLs forward slash append
- Add events to `TransactionWorker` module that dapps can listen to
- Introduce `aptos.transaction.batch` namespace to handle batch transactions
- Support `aptos.transaction.batch.forSingleAccount()` to send batch transactions for a single account
- Label `aptos.batchTransactionsForSingleAccount()` as `deprecated` to prefer using `aptos.transaction.batch.forSingleAccount()`

# 1.4.0 (2024-01-08)

- Omit `"build" | "simulate" | "submit"` from `aptos` namespace
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ const pendingTransaction = await aptos.signAndSubmitTransaction({ signer: alice,
## Documentation and examples
- For full SDK documentation, check out the [TypeScript SDK documentation](https://aptos.dev/sdks/ts-sdk-v2/)
- For full SDK documentation, check out the [TypeScript SDK documentation](https://aptos.dev/sdks/new-ts-sdk/)
- For reference documenation, check out the [API reference documentation](https://aptos-labs.github.io/aptos-ts-sdk/) for the associated version.
- For in-depth examples, check out the [examples](./examples) folder with ready-made `package.json` files to get you going quickly!
Expand Down
32 changes: 28 additions & 4 deletions examples/typescript/batch_funds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,17 @@ import {
InputGenerateTransactionPayloadData,
Network,
NetworkToNetworkName,
TransactionWorkerEventsEnum,
UserTransactionResponse,
} from "@aptos-labs/ts-sdk";

const APTOS_NETWORK: Network = NetworkToNetworkName[process.env.APTOS_NETWORK] || Network.DEVNET;
const APTOS_NETWORK: Network = NetworkToNetworkName[process.env.APTOS_NETWORK] || Network.LOCAL;

const config = new AptosConfig({ network: APTOS_NETWORK });
const aptos = new Aptos(config);

async function main() {
const accountsCount = 1;
const accountsCount = 2;
const transactionsCount = 10;
const totalTransactions = accountsCount * transactionsCount;

Expand Down Expand Up @@ -97,8 +98,31 @@ async function main() {

console.log(`sends ${totalTransactions * senders.length} transactions to ${aptos.config.network}....`);
// emit batch transactions
const promises = senders.map((sender) => aptos.batchTransactionsForSingleAccount({ sender, data: payloads }));
await Promise.all(promises);
senders.map((sender) => aptos.transaction.batch.forSingleAccount({ sender, data: payloads }));

aptos.transaction.batch.on(TransactionWorkerEventsEnum.TransactionSent, async (data) => {
console.log("message:", data.message);
console.log("transaction hash:", data.transactionHash);
});

aptos.transaction.batch.on(TransactionWorkerEventsEnum.ExecutionFinish, async (data) => {
// log event output
console.log(data.message);

// verify accounts sequence number
const accounts = senders.map((sender) => aptos.getAccountInfo({ accountAddress: sender.accountAddress }));
const accountsData = await Promise.all(accounts);
accountsData.forEach((accountData) => {
console.log(
`account sequence number is ${(totalTransactions * senders.length) / 2}: ${
accountData.sequence_number === "20"
}`,
);
});
// worker finished execution, we can now unsubscribe from event listeners
aptos.transaction.batch.removeAllListeners();
process.exit(0);
});
}

main();
16 changes: 15 additions & 1 deletion examples/typescript/batch_mint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
Network,
NetworkToNetworkName,
UserTransactionResponse,
TransactionWorkerEventsEnum,
} from "@aptos-labs/ts-sdk";

const APTOS_NETWORK: Network = NetworkToNetworkName[process.env.APTOS_NETWORK] || Network.DEVNET;
Expand Down Expand Up @@ -77,7 +78,20 @@ async function main() {
}

// batch mint token transactions
aptos.batchTransactionsForSingleAccount({ sender, data: payloads });
aptos.transaction.batch.forSingleAccount({ sender, data: payloads });

aptos.transaction.batch.on(TransactionWorkerEventsEnum.ExecutionFinish, async (data) => {
// log event output
console.log(data);

// verify account sequence number
const account = await aptos.getAccountInfo({ accountAddress: sender.accountAddress });
console.log(`account sequence number is 101: ${account.sequence_number === "101"}`);

// worker finished execution, we can now unsubscribe from event listeners
aptos.transaction.batch.removeAllListeners();
process.exit(0);
});
}

main();
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"@typescript-eslint/eslint-plugin": "^6.14.0",
"@typescript-eslint/parser": "^6.14.0",
"dotenv": "^16.3.1",
"eventemitter3": "^5.0.1",
"eslint": "^8.55.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-airbnb-typescript": "^17.1.0",
Expand Down
7 changes: 7 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/api/aptos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export interface Aptos
FungibleAsset,
General,
Staking,
Omit<Transaction, "build" | "simulate" | "submit"> {}
Omit<Transaction, "build" | "simulate" | "submit" | "batch"> {}

/**
In TypeScript, we can’t inherit or extend from more than one class,
Expand Down
16 changes: 7 additions & 9 deletions src/api/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ import {
SimpleTransaction,
} from "../transactions";
import { AccountAddressInput, Account, PrivateKey } from "../core";
import { TransactionWorker } from "../transactions/management";
import { Build } from "./transactionSubmission/build";
import { Simulate } from "./transactionSubmission/simulate";
import { Submit } from "./transactionSubmission/submit";
import { TransactionManagement } from "./transactionSubmission/management";

export class Transaction {
readonly config: AptosConfig;
Expand All @@ -49,11 +49,14 @@ export class Transaction {

readonly submit: Submit;

readonly batch: TransactionManagement;

constructor(config: AptosConfig) {
this.config = config;
this.build = new Build(this.config);
this.simulate = new Simulate(this.config);
this.submit = new Submit(this.config);
this.batch = new TransactionManagement(this.config);
}

/**
Expand Down Expand Up @@ -263,6 +266,8 @@ export class Transaction {
// TRANSACTION SUBMISSION //

/**
* @deprecated Prefer to use `aptos.transaction.batch.forSingleAccount()`
*
* Batch transactions for a single account.
*
* This function uses a transaction worker that receives payloads to be processed
Expand All @@ -285,14 +290,7 @@ export class Transaction {
}): Promise<void> {
try {
const { sender, data, options } = args;
const transactionWorker = new TransactionWorker(this.config, sender);

transactionWorker.start();

for (const d of data) {
/* eslint-disable no-await-in-loop */
await transactionWorker.push(d, options);
}
this.batch.forSingleAccount({ sender, data, options });
} catch (error: any) {
throw new Error(`failed to submit transactions with error: ${error}`);
}
Expand Down
106 changes: 106 additions & 0 deletions src/api/transactionSubmission/management.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import EventEmitter from "eventemitter3";
import { TransactionWorkerEvents, TransactionWorker, TransactionWorkerEventsEnum } from "../../transactions/management";
import { InputGenerateTransactionPayloadData, InputGenerateTransactionOptions } from "../../transactions";
import { AptosConfig } from "../aptosConfig";
import { Account } from "../../core";

export class TransactionManagement extends EventEmitter<TransactionWorkerEvents> {
account!: Account;

transactionWorker!: TransactionWorker;

readonly config: AptosConfig;

constructor(config: AptosConfig) {
super();
this.config = config;
}

/**
* Internal function to start the transaction worker and
* listen to worker events
*
* @param args.sender The sender account to sign and submit the transaction
*/
private start(args: { sender: Account }): void {
const { sender } = args;
this.account = sender;
this.transactionWorker = new TransactionWorker(this.config, sender);

this.transactionWorker.start();
this.registerToEvents();
}

/**
* Internal function to push transaction data to the transaction worker.
*
* @param args.data An array of transaction payloads
* @param args.options optional. Transaction generation configurations (excluding accountSequenceNumber)
*
* TODO - make this public once worker supports adding transactions to existing queue
*/
private push(args: {
data: InputGenerateTransactionPayloadData[];
options?: Omit<InputGenerateTransactionOptions, "accountSequenceNumber">;
}): void {
const { data, options } = args;

for (const d of data) {
this.transactionWorker.push(d, options);
}
}

/**
* Internal function to start listening to transaction worker events
*
* TODO - should we ask events to listen to as an input?
*/
private registerToEvents() {
this.transactionWorker.on(TransactionWorkerEventsEnum.TransactionSent, async (data) => {
this.emit(TransactionWorkerEventsEnum.TransactionSent, data);
});
this.transactionWorker.on(TransactionWorkerEventsEnum.TransactionSendFailed, async (data) => {
this.emit(TransactionWorkerEventsEnum.TransactionSendFailed, data);
});
this.transactionWorker.on(TransactionWorkerEventsEnum.TransactionExecuted, async (data) => {
this.emit(TransactionWorkerEventsEnum.TransactionExecuted, data);
});
this.transactionWorker.on(TransactionWorkerEventsEnum.TransactionExecutionFailed, async (data) => {
this.emit(TransactionWorkerEventsEnum.TransactionExecutionFailed, data);
});
this.transactionWorker.on(TransactionWorkerEventsEnum.ExecutionFinish, async (data) => {
this.emit(TransactionWorkerEventsEnum.ExecutionFinish, data);
});
}

/**
* Send batch transactions for a single account.
*
* This function uses a transaction worker that receives payloads to be processed
* and submitted to chain.
* Note that this process is best for submitting multiple transactions that
* dont rely on each other, i.e batch funds, batch token mints, etc.
*
* If any worker failure, the functions throws an error.
*
* @param args.sender The sender account to sign and submit the transaction
* @param args.data An array of transaction payloads
* @param args.options optional. Transaction generation configurations (excluding accountSequenceNumber)
*
* @return void. Throws if any error
*/
forSingleAccount(args: {
sender: Account;
data: InputGenerateTransactionPayloadData[];
options?: Omit<InputGenerateTransactionOptions, "accountSequenceNumber">;
}): void {
try {
const { sender, data, options } = args;
this.start({ sender });

this.push({ data, options });
} catch (error: any) {
throw new Error(`failed to submit transactions with error: ${error}`);
}
}
}
2 changes: 1 addition & 1 deletion src/client/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export async function aptosRequest<Req extends {}, Res extends {}>(
aptosConfig: AptosConfig,
): Promise<AptosResponse<Req, Res>> {
const { url, path } = options;
const fullUrl = `${url}/${path ?? ""}`;
const fullUrl = path ? `${url}/${path}` : url;
const response = await request<Req, Res>({ ...options, url: fullUrl }, aptosConfig.client);

const result: AptosResponse<Req, Res> = {
Expand Down
Loading

0 comments on commit d114a05

Please sign in to comment.