diff --git a/src/api/transaction_submission.ts b/src/api/transaction_submission.ts index 90fd3a4c0..c5f7b4682 100644 --- a/src/api/transaction_submission.ts +++ b/src/api/transaction_submission.ts @@ -4,7 +4,6 @@ import { AptosConfig } from "./aptos_config"; import { Account } from "../core/account"; import { AccountAuthenticator } from "../transactions/authenticator/account"; -import { MimeType, postAptosFullNode } from "../client"; import { AnyRawTransaction, GenerateMultiAgentRawTransactionInput, @@ -18,12 +17,11 @@ import { } from "../transactions/types"; import { UserTransactionResponse, PendingTransactionResponse } from "../types"; import { - generateSignedTransaction, - generateSignedTransactionForSimulation, generateTransaction, - generateTransactionPayload, signTransaction, -} from "../transactions/transaction_builder/transaction_builder"; + simulateTransaction, + submitTransaction, +} from "../internal/transaction_submission"; export class TransactionSubmission { readonly config: AptosConfig; @@ -84,17 +82,8 @@ export class TransactionSubmission { * ``` */ async generateTransaction(args: GenerateTransactionInput): Promise { - const { sender, data, options, secondarySignerAddresses, feePayerAddress } = args; - const payload = await generateTransactionPayload(data); - const rawTransaction = await generateTransaction({ - aptosConfig: this.config, - sender, - payload, - options, - secondarySignerAddresses, - feePayerAddress, - }); - return rawTransaction; + const transaction = await generateTransaction({ aptosConfig: this.config, ...args }); + return transaction; } /** @@ -128,21 +117,7 @@ export class TransactionSubmission { * @param options optional. A config to simulate the transaction with */ async simulateTransaction(args: SimulateTransactionData): Promise> { - const signedTransaction = generateSignedTransactionForSimulation({ - ...args, - }); - const { data } = await postAptosFullNode>({ - aptosConfig: this.config, - body: signedTransaction, - path: "transactions/simulate", - params: { - estimate_gas_unit_price: args.options?.estimateGasUnitPrice ?? false, - estimate_max_gas_amount: args.options?.estimateMaxGasAmount ?? false, - estimate_prioritized_gas_unit_price: args.options?.estimatePrioritizedGasUnitPrice ?? false, - }, - originMethod: "simulateTransaction", - contentType: MimeType.BCS_SIGNED_TRANSACTION, - }); + const data = await simulateTransaction({ aptosConfig: this.config, ...args }); return data; } @@ -163,14 +138,7 @@ export class TransactionSubmission { additionalSignersAuthenticators?: Array; }; }): Promise { - const signedTransaction = generateSignedTransaction({ ...args }); - const { data } = await postAptosFullNode({ - aptosConfig: this.config, - body: signedTransaction, - path: "transactions", - originMethod: "submitTransaction", - contentType: MimeType.BCS_SIGNED_TRANSACTION, - }); + const data = await submitTransaction({ aptosConfig: this.config, ...args }); return data; } } diff --git a/src/client/core.ts b/src/client/core.ts index 57ac90d2c..08e040223 100644 --- a/src/client/core.ts +++ b/src/client/core.ts @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import aptosClient from "@aptos-labs/aptos-client"; -import { AptosApiError, AptosResponse, MimeType } from "./types"; +import { AptosApiError, AptosResponse } from "./types"; import { VERSION } from "../version"; -import { ClientConfig, AptosRequest } from "../types"; +import { ClientConfig, AptosRequest, MimeType } from "../types"; import { AptosConfig } from "../api/aptos_config"; /** diff --git a/src/client/get.ts b/src/client/get.ts index 3320846b5..a5a8fdb15 100644 --- a/src/client/get.ts +++ b/src/client/get.ts @@ -1,9 +1,6 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -import { AptosResponse, MimeType } from "./types"; +import { AptosResponse } from "./types"; import { aptosRequest } from "./core"; -import { AnyNumber, ClientConfig } from "../types"; +import { AnyNumber, ClientConfig, MimeType } from "../types"; import { AptosConfig } from "../api/aptos_config"; import { AptosApiType } from "../utils/const"; diff --git a/src/client/post.ts b/src/client/post.ts index 420fc787c..d74050fb0 100644 --- a/src/client/post.ts +++ b/src/client/post.ts @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import { AptosConfig } from "../api/aptos_config"; -import { AnyNumber, ClientConfig } from "../types"; +import { AnyNumber, ClientConfig, MimeType } from "../types"; import { aptosRequest } from "./core"; -import { AptosResponse, MimeType } from "./types"; +import { AptosResponse } from "./types"; import { AptosApiType } from "../utils/const"; export type PostRequestOptions = { diff --git a/src/client/types.ts b/src/client/types.ts index 5dd5f0c99..27c6f7b62 100644 --- a/src/client/types.ts +++ b/src/client/types.ts @@ -56,18 +56,3 @@ export class AptosApiError extends Error { this.request = request; } } - -export enum MimeType { - /** - * JSON representation, used for transaction submission and accept type JSON output - */ - JSON = "application/json", - /** - * BCS representation, used for accept type BCS output - */ - BCS = "application/x-bcs", - /** - * BCS representation, used for transaction submission in BCS input - */ - BCS_SIGNED_TRANSACTION = "application/x.aptos.signed_transaction+bcs", -} diff --git a/src/internal/transaction_submission.ts b/src/internal/transaction_submission.ts new file mode 100644 index 000000000..18502144f --- /dev/null +++ b/src/internal/transaction_submission.ts @@ -0,0 +1,163 @@ +/** + * This file contains the underlying implementations for exposed API surface in + * the {@link api/transaction_submission}. By moving the methods out into a separate file, + * other namespaces and processes can access these methods without depending on the entire + * transaction_submission namespace and without having a dependency cycle error. + */ + +import { AptosConfig } from "../api/aptos_config"; +import { postAptosFullNode } from "../client"; +import { Account } from "../core/account"; +import { AccountAuthenticator } from "../transactions/authenticator/account"; +import { + buildTransaction, + generateTransactionPayload, + generateSignedTransactionForSimulation, + generateSignedTransaction, + sign, +} from "../transactions/transaction_builder/transaction_builder"; +import { GenerateTransactionInput, AnyRawTransaction, SimulateTransactionData } from "../transactions/types"; +import { UserTransactionResponse, PendingTransactionResponse, MimeType } from "../types"; + +/** + * Generates any transaction by passing in the required arguments + * + * @param args.sender The transaction sender's account address as a HexInput + * @param args.data EntryFunctionData | ScriptData | MultiSigData + * @param feePayerAddress optional. For a fee payer (aka sponsored) transaction + * @param secondarySignerAddresses optional. For a multi agent or fee payer (aka sponsored) transactions + * @param args.options optional. GenerateTransactionOptions type + * + * @example + * For a single signer entry function + * move function name, move function type arguments, move function arguments + * ` + * data: { + * function:"0x1::aptos_account::transfer", + * type_arguments:[] + * arguments:[recieverAddress,10] + * } + * ` + * + * @example + * For a single signer script function + * module bytecode, move function type arguments, move function arguments + * ``` + * data: { + * bytecode:"0x001234567", + * type_arguments:[], + * arguments:[recieverAddress,10] + * } + * ``` + * + * @return A raw transaction type (note that it holds the raw transaction as a bcs serialized data) + * ``` + * { + * rawTransaction: Uint8Array, + * secondarySignerAddresses? : Array, + * feePayerAddress?: AccountAddress + * } + * ``` + */ +export async function generateTransaction( + args: { aptosConfig: AptosConfig } & GenerateTransactionInput, +): Promise { + const { aptosConfig, sender, data, options, secondarySignerAddresses, feePayerAddress } = args; + const payload = await generateTransactionPayload(data); + const rawTransaction = await buildTransaction({ + aptosConfig, + sender, + payload, + options, + secondarySignerAddresses, + feePayerAddress, + }); + return rawTransaction; +} + +/** + * Sign a transaction that can later be submitted to chain + * + * @param args.signer The signer account to sign the transaction + * @param args.transaction A raw transaction type (note that it holds the raw transaction as a bcs serialized data) + * ``` + * { + * rawTransaction: Uint8Array, + * secondarySignerAddresses? : Array, + * feePayerAddress?: AccountAddress + * } + * ``` + * + * @return The signer AccountAuthenticator + */ +export function signTransaction(args: { signer: Account; transaction: AnyRawTransaction }): AccountAuthenticator { + const accountAuthenticator = sign({ ...args }); + return accountAuthenticator; +} + +/** + * Simulates a transaction before singing it. + * + * @param signerPublicKey The signer pubic key + * @param transaction The raw transaction to simulate + * @param secondarySignersPublicKeys optional. For when the transaction is a multi signers transaction + * @param feePayerPublicKey optional. For when the transaction is a fee payer (aka sponsored) transaction + * @param options optional. A config to simulate the transaction with + */ +export async function simulateTransaction( + args: { aptosConfig: AptosConfig } & SimulateTransactionData, +): Promise> { + const { aptosConfig, transaction, signerPublicKey, secondarySignersPublicKeys, feePayerPublicKey, options } = args; + + const signedTransaction = generateSignedTransactionForSimulation({ + transaction, + signerPublicKey, + secondarySignersPublicKeys, + feePayerPublicKey, + options, + }); + + const { data } = await postAptosFullNode>({ + aptosConfig, + body: signedTransaction, + path: "transactions/simulate", + params: { + estimate_gas_unit_price: args.options?.estimateGasUnitPrice ?? false, + estimate_max_gas_amount: args.options?.estimateMaxGasAmount ?? false, + estimate_prioritized_gas_unit_price: args.options?.estimatePrioritizedGasUnitPrice ?? false, + }, + originMethod: "simulateTransaction", + contentType: MimeType.BCS_SIGNED_TRANSACTION, + }); + return data; +} + +/** + * Submit transaction to chain + * + * @param args.transaction A aptos transaction type + * @param args.senderAuthenticator The account authenticator of the transaction sender + * @param args.secondarySignerAuthenticators optional. For when the transaction is a multi signers transaction + * + * @return PendingTransactionResponse + */ +export async function submitTransaction(args: { + aptosConfig: AptosConfig; + transaction: AnyRawTransaction; + senderAuthenticator: AccountAuthenticator; + secondarySignerAuthenticators?: { + feePayerAuthenticator?: AccountAuthenticator; + additionalSignersAuthenticators?: Array; + }; +}): Promise { + const { aptosConfig } = args; + const signedTransaction = generateSignedTransaction({ ...args }); + const { data } = await postAptosFullNode({ + aptosConfig, + body: signedTransaction, + path: "transactions", + originMethod: "submitTransaction", + contentType: MimeType.BCS_SIGNED_TRANSACTION, + }); + return data; +} diff --git a/src/transactions/transaction_builder/transaction_builder.ts b/src/transactions/transaction_builder/transaction_builder.ts index b7a1b81c1..7eb43bf91 100644 --- a/src/transactions/transaction_builder/transaction_builder.ts +++ b/src/transactions/transaction_builder/transaction_builder.ts @@ -19,7 +19,12 @@ import { getLedgerInfo } from "../../internal/general"; import { getGasPriceEstimation } from "../../internal/transaction"; import { HexInput, SigningScheme } from "../../types"; import { NetworkToChainId } from "../../utils/apiEndpoints"; -import { DEFAULT_MAX_GAS_AMOUNT, DEFAULT_TXN_EXP_SEC_FROM_NOW } from "../../utils/const"; +import { + DEFAULT_MAX_GAS_AMOUNT, + DEFAULT_TXN_EXP_SEC_FROM_NOW, + RAW_TRANSACTION_SALT, + RAW_TRANSACTION_WITH_DATA_SALT, +} from "../../utils/const"; import { AccountAuthenticator, AccountAuthenticatorEd25519, @@ -180,12 +185,10 @@ export async function generateRawTransaction(args: { * When we call our `generateTransaction` function with the relevant type properties, * Typescript can infer the return type based on the appropriate function overload. */ -export async function generateTransaction( - args: GenerateSingleSignerRawTransactionArgs, -): Promise; -export async function generateTransaction(args: GenerateFeePayerRawTransactionArgs): Promise; -export async function generateTransaction(args: GenerateMultiAgentRawTransactionArgs): Promise; -export async function generateTransaction(args: GenerateRawTransactionArgs): Promise; +export async function buildTransaction(args: GenerateSingleSignerRawTransactionArgs): Promise; +export async function buildTransaction(args: GenerateFeePayerRawTransactionArgs): Promise; +export async function buildTransaction(args: GenerateMultiAgentRawTransactionArgs): Promise; +export async function buildTransaction(args: GenerateRawTransactionArgs): Promise; /** * Generates a transaction based on the provided arguments * @@ -208,7 +211,7 @@ export async function generateTransaction(args: GenerateRawTransactionArgs): Pro * } * ``` */ -export async function generateTransaction(args: GenerateRawTransactionArgs): Promise { +export async function buildTransaction(args: GenerateRawTransactionArgs): Promise { const { aptosConfig, sender, payload, options, secondarySignerAddresses, feePayerAddress } = args; // generate raw transaction const rawTxn = await generateRawTransaction({ @@ -246,8 +249,14 @@ export async function generateTransaction(args: GenerateRawTransactionArgs): Pro /** * Simluate a transaction before signing and submit to chain - * @param args - * @returns + * + * @param args.transaction A aptos transaction type to sign + * @param args.signerPublicKey The signer public key + * @param args.secondarySignersPublicKeys optional. The secondart signers public keys if multi signers transaction + * @param args.feePayerPublicKey optional. The fee payer public key is a fee payer (aka sponsored) transaction + * @param args.options optional. SimulateTransactionOptions + * + * @returns A signed serialized transaction that can be simulated */ export function generateSignedTransactionForSimulation(args: SimulateTransactionData): Uint8Array { const { signerPublicKey, transaction, secondarySignersPublicKeys, feePayerPublicKey } = args; @@ -340,7 +349,7 @@ export function generateSignedTransactionForSimulation(args: SimulateTransaction * * @return The signer AccountAuthenticator */ -export function signTransaction(args: { signer: Account; transaction: AnyRawTransaction }): AccountAuthenticator { +export function sign(args: { signer: Account; transaction: AnyRawTransaction }): AccountAuthenticator { const { signer, transaction } = args; const transactionToSign = deriveTransactionType(transaction); @@ -505,9 +514,6 @@ export function generateMultiSignersSignedTransaction( ); } -const RAW_TRANSACTION_SALT = "APTOS::RawTransaction"; -const RAW_TRANSACTION_WITH_DATA_SALT = "APTOS::RawTransactionWithData"; - export function getSigningMessage(rawTxn: AnyRawTransactionInstance): Uint8Array { const hash = sha3Hash.create(); diff --git a/src/types/index.ts b/src/types/index.ts index 3baf491ed..00d787cc5 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -5,6 +5,21 @@ import { Network } from "../utils/apiEndpoints"; export * from "./indexer"; +export enum MimeType { + /** + * JSON representation, used for transaction submission and accept type JSON output + */ + JSON = "application/json", + /** + * BCS representation, used for accept type BCS output + */ + BCS = "application/x-bcs", + /** + * BCS representation, used for transaction submission in BCS input + */ + BCS_SIGNED_TRANSACTION = "application/x.aptos.signed_transaction+bcs", +} + /** * Hex data as input to a function */ diff --git a/src/utils/const.ts b/src/utils/const.ts index 84ec138b7..64108cbfa 100644 --- a/src/utils/const.ts +++ b/src/utils/const.ts @@ -15,3 +15,6 @@ export enum AptosApiType { export const DEFAULT_MAX_GAS_AMOUNT = 200000; // Transaction expire timestamp export const DEFAULT_TXN_EXP_SEC_FROM_NOW = 20; +export const APTOS_COIN = "0x1::aptos_coin::AptosCoin"; +export const RAW_TRANSACTION_SALT = "APTOS::RawTransaction"; +export const RAW_TRANSACTION_WITH_DATA_SALT = "APTOS::RawTransactionWithData"; diff --git a/tests/unit/transaction_builder.test.ts b/tests/unit/transaction_builder.test.ts index 3eafda779..a0cb14829 100644 --- a/tests/unit/transaction_builder.test.ts +++ b/tests/unit/transaction_builder.test.ts @@ -16,13 +16,13 @@ import { TransactionPayloadScript, } from "../../src/transactions/instances"; import { + buildTransaction, deriveTransactionType, generateRawTransaction, generateSignedTransaction, generateSignedTransactionForSimulation, - generateTransaction, generateTransactionPayload, - signTransaction, + sign, } from "../../src/transactions/transaction_builder/transaction_builder"; import { SigningScheme } from "../../src/types"; import { SignedTransaction } from "../../src/transactions/instances/signedTransaction"; @@ -158,7 +158,7 @@ describe("transaction builder", () => { new ScriptTransactionArgumentU64(BigInt(50)), ], }); - const transaction = await generateTransaction({ + const transaction = await buildTransaction({ aptosConfig: config, sender: alice.accountAddress.toString(), payload, @@ -184,7 +184,7 @@ describe("transaction builder", () => { const secondarySignerAddress = Account.generate({ scheme: SigningScheme.Ed25519, }); - const transaction = await generateTransaction({ + const transaction = await buildTransaction({ aptosConfig: config, sender: alice.accountAddress.toString(), payload, @@ -213,7 +213,7 @@ describe("transaction builder", () => { arguments: [new MoveObject(bob.accountAddress), new U64(1)], }); const feePayer = Account.generate({ scheme: SigningScheme.Ed25519 }); - const transaction = await generateTransaction({ + const transaction = await buildTransaction({ aptosConfig: config, sender: alice.accountAddress.toString(), payload, @@ -242,7 +242,7 @@ describe("transaction builder", () => { const secondarySignerAddress = Account.generate({ scheme: SigningScheme.Ed25519, }); - const transaction = await generateTransaction({ + const transaction = await buildTransaction({ aptosConfig: config, sender: alice.accountAddress.toString(), payload, @@ -279,7 +279,7 @@ describe("transaction builder", () => { new ScriptTransactionArgumentU64(BigInt(50)), ], }); - const transaction = await generateTransaction({ + const transaction = await buildTransaction({ aptosConfig: config, sender: alice.accountAddress.toString(), payload, @@ -315,12 +315,12 @@ describe("transaction builder", () => { new ScriptTransactionArgumentU64(BigInt(50)), ], }); - const transaction = await generateTransaction({ + const transaction = await buildTransaction({ aptosConfig: config, sender: alice.accountAddress.toString(), payload, }); - const accountAuthenticator = signTransaction({ + const accountAuthenticator = sign({ signer: alice, transaction, }); @@ -344,7 +344,7 @@ describe("transaction builder", () => { type_arguments: [], arguments: [new MoveObject(bob.accountAddress), new U64(1)], }); - const transaction = await generateTransaction({ + const transaction = await buildTransaction({ aptosConfig: config, sender: alice.accountAddress.toString(), payload, @@ -352,7 +352,7 @@ describe("transaction builder", () => { scheme: SigningScheme.Ed25519, }).accountAddress.toString(), }); - const accountAuthenticator = signTransaction({ + const accountAuthenticator = sign({ signer: alice, transaction, }); @@ -382,13 +382,13 @@ describe("transaction builder", () => { new ScriptTransactionArgumentU64(BigInt(50)), ], }); - const rawTxn = await generateTransaction({ + const rawTxn = await buildTransaction({ aptosConfig: config, sender: alice.accountAddress.toString(), payload, secondarySignerAddresses: [bob.accountAddress.toString()], }); - const accountAuthenticator = signTransaction({ + const accountAuthenticator = sign({ signer: alice, transaction: rawTxn, }); @@ -418,12 +418,12 @@ describe("transaction builder", () => { new ScriptTransactionArgumentU64(BigInt(50)), ], }); - const transaction = await generateTransaction({ + const transaction = await buildTransaction({ aptosConfig: config, sender: alice.accountAddress.toString(), payload, }); - const authenticator = signTransaction({ signer: alice, transaction }); + const authenticator = sign({ signer: alice, transaction }); const bcsTransaction = await generateSignedTransaction({ transaction, senderAuthenticator: authenticator, @@ -447,14 +447,14 @@ describe("transaction builder", () => { type_arguments: [], arguments: [new MoveObject(bob.accountAddress), new U64(1)], }); - const transaction = await generateTransaction({ + const transaction = await buildTransaction({ aptosConfig: config, sender: alice.accountAddress.toString(), payload, secondarySignerAddresses: [bob.accountAddress.toString()], }); - const authenticator = signTransaction({ signer: alice, transaction }); - const secondaryAuthenticator = signTransaction({ + const authenticator = sign({ signer: alice, transaction }); + const secondaryAuthenticator = sign({ signer: bob, transaction, }); @@ -484,14 +484,14 @@ describe("transaction builder", () => { type_arguments: [], arguments: [new MoveObject(bob.accountAddress), new U64(1)], }); - const transaction = await generateTransaction({ + const transaction = await buildTransaction({ aptosConfig: config, sender: alice.accountAddress.toString(), payload, feePayerAddress: bob.accountAddress.toString(), }); - const authenticator = signTransaction({ signer: alice, transaction }); - const feePayerAuthenticator = signTransaction({ + const authenticator = sign({ signer: alice, transaction }); + const feePayerAuthenticator = sign({ signer: bob, transaction, }); @@ -522,7 +522,7 @@ describe("transaction builder", () => { type_arguments: [], arguments: [new MoveObject(bob.accountAddress), new U64(1)], }); - const transaction = await generateTransaction({ + const transaction = await buildTransaction({ aptosConfig: config, sender: alice.accountAddress.toString(), payload, @@ -544,7 +544,7 @@ describe("transaction builder", () => { type_arguments: [], arguments: [new MoveObject(bob.accountAddress), new U64(1)], }); - const transaction = await generateTransaction({ + const transaction = await buildTransaction({ aptosConfig: config, sender: alice.accountAddress.toString(), payload, @@ -570,7 +570,7 @@ describe("transaction builder", () => { type_arguments: [], arguments: [new MoveObject(bob.accountAddress), new U64(1)], }); - const transaction = await generateTransaction({ + const transaction = await buildTransaction({ aptosConfig: config, sender: alice.accountAddress.toString(), payload,