@pact-toolbox/transaction
v0.2.0
Published
> High-level transaction builder for Pact smart contracts
Maintainers
Readme
@pact-toolbox/transaction
High-level transaction builder for Pact smart contracts
Overview
The @pact-toolbox/transaction package provides a powerful, type-safe transaction builder for Pact blockchains. It features a fluent transaction builder API, multi-wallet support, network context management, and convenient abstractions for common operations.
Installation
npm install @pact-toolbox/transaction
# or
pnpm add @pact-toolbox/transactionFeatures
- 🔧 Fluent Transaction Builder - Chainable API for building complex transactions
- 🔐 Multi-Wallet Support - Integrates with Ecko, Ledger, MetaMask, and more
- 🎯 Type-Safe - Full TypeScript support with proper typing
- 📦 High-Level Abstractions - Simplified APIs for common operations
- ⚡ Transaction Management - Automatic polling and confirmation handling
- 🌐 Cross-Platform - Works in Node.js, browsers, and React Native
Quick Start
import { execution } from "@pact-toolbox/transaction";
// Build and execute a simple read operation
const result = await execution('(coin.details "alice")')
.withChainId("0")
.build()
.dirtyRead();
console.log(result);Transaction Builder
Basic Usage
import { execution } from "@pact-toolbox/transaction";
// Build and execute a transaction
const result = await execution('(coin.transfer "alice" "bob" 10.0)')
.withSigner("alice-public-key", (signFor) => [
signFor("coin.TRANSFER", "alice", "bob", 10.0)
])
.withChainId("0")
.withMeta({
gasLimit: 1000,
gasPrice: 0.00001,
})
.sign(wallet)
.submitAndListen();Continuation Transactions
import { continuation } from "@pact-toolbox/transaction";
// Create a continuation transaction
const contResult = await continuation({
pactId: "cross-chain-transfer-pact-id",
step: 1,
rollback: false,
data: { amount: 10.0 },
})
.withChainId("1")
.sign(wallet)
.submitAndListen();Data and Keysets
const txWithData = execution("(free.my-module.process-data data)")
.withData("user", "alice")
.withData("amount", 100)
.withKeyset("admin-keyset", {
keys: ["admin-public-key"],
pred: "keys-all",
})
.withChainId("0");Wallet Integration
Using Wallets with Transactions
The package supports explicit wallet selection for enhanced security:
import { execution } from "@pact-toolbox/transaction";
// Option 1: Provide wallet instance
const result = await execution('(coin.transfer "alice" "bob" 10.0)')
.withChainId("0")
.sign(myWallet) // Pass wallet instance
.submitAndListen();
// Option 2: Use wallet ID (requires wallet adapters)
const result2 = await execution('(coin.transfer "alice" "bob" 10.0)')
.withChainId("0")
.sign("ecko-wallet", { showUI: true }) // Show wallet selector
.submitAndListen();
// Option 3: Show wallet selector
const result3 = await execution('(coin.transfer "alice" "bob" 10.0)')
.withChainId("0")
.sign() // Shows wallet selector UI
.submitAndListen();Network Context Management
The transaction package automatically manages network configuration through a global context:
Getting Network Context
import { createToolboxNetworkContext, getToolboxGlobalMultiNetworkConfig } from "@pact-toolbox/transaction";
// Get global network configuration
const config = getToolboxGlobalMultiNetworkConfig();
console.log("Available networks:", Object.keys(config.configs));
console.log("Current environment:", config.environment);
// Create a network context
const context = createToolboxNetworkContext(config);Using Custom Network Context
import { execution, createToolboxNetworkContext } from "@pact-toolbox/transaction";
// Create custom context
const customConfig = {
default: "testnet",
environment: "development",
configs: {
testnet: {
type: "chainweb",
networkId: "testnet04",
rpcUrl: "https://api.testnet.chainweb.com/chainweb/0.0/testnet04/chain/{chainId}/pact",
meta: { chainId: "0" },
// ... other config
}
}
};
const context = createToolboxNetworkContext(customConfig);
// Use with transactions
const result = await execution('(coin.details "alice")', context)
.withChainId("0")
.build()
.dirtyRead();Transaction Execution Methods
Local Operations (Read-only)
// dirtyRead - Fast read without going through consensus
const balance = await execution('(coin.get-balance "alice")')
.withChainId("0")
.build()
.dirtyRead();
// local - Full local execution with gas estimation
const result = await execution('(coin.transfer "alice" "bob" 10.0)')
.withSigner("alice-key", (signFor) => [signFor("coin.TRANSFER", "alice", "bob", 10.0)])
.withChainId("0")
.build()
.local();Write Operations
// submit - Submit transaction and get request key
const descriptor = await execution('(coin.transfer "alice" "bob" 10.0)')
.withSigner("alice-key", (signFor) => [signFor("coin.TRANSFER", "alice", "bob", 10.0)])
.withChainId("0")
.sign(wallet)
.submit();
console.log("Request key:", descriptor.requestKey);
// submitAndListen - Submit and wait for result
const result = await execution('(coin.transfer "alice" "bob" 10.0)')
.withSigner("alice-key", (signFor) => [signFor("coin.TRANSFER", "alice", "bob", 10.0)])
.withChainId("0")
.sign(wallet)
.submitAndListen();Multi-Chain Operations
// Execute on multiple chains
const multiChainResult = await execution('(coin.get-balance "alice")')
.build()
.dirtyRead(["0", "1", "2"]); // Execute on chains 0, 1, and 2
// Execute on all chains (0-19)
const allChainResult = await execution('(coin.get-balance "alice")')
.build()
.dirtyReadAll();Utility Functions
Account Generation
import { generateKAccount, generateKAccounts } from "@pact-toolbox/transaction";
// Generate a single K-account
const account = await generateKAccount();
console.log(account.account); // "k:public-key..."
console.log(account.publicKey);
console.log(account.secretKey);
// Generate multiple accounts
const accounts = await generateKAccounts(5);Network Validation
import { validateNetworkForEnvironment } from "@pact-toolbox/transaction";
// Check if network is allowed in current environment
const isValid = validateNetworkForEnvironment("pactServer");
if (!isValid) {
console.log("Network not available in production");
}Pact Decimal Formatting
import { pactDecimal } from "@pact-toolbox/transaction";
const amount = pactDecimal(123.456);
console.log(amount.decimal); // "123.456000000000"Transaction Builder Methods
Data Methods
withData(key: string, value: Serializable)- Add data key-value pairwithDataMap(data: Record<string, Serializable>)- Add multiple data entries
Keyset Methods
withKeyset(name: string, keyset: PactKeyset)- Add keysetwithKeysetMap(keysets: Record<string, PactKeyset>)- Add multiple keysets
Transaction Configuration
withChainId(chainId: ChainId)- Set chain IDwithMeta(meta: Partial<PactMetadata>)- Set transaction metadatawithNetworkId(networkId: string)- Set network IDwithNonce(nonce: string)- Set custom nonce
Signer and Verifier Methods
withSigner(signer: PactSignerLike, capability?: PactCapabilityLike)- Add signer with optional capabilitieswithVerifier(verifier: PactVerifier)- Add verifier
Context and Building
withContext(context: ToolboxNetworkContext)- Set network contextbuild(context?: ToolboxNetworkContext)- Build unsigned transaction dispatchersign(walletOrId?: Wallet | string, options?: WalletUIOptions)- Sign and build transaction dispatcher
PactTransactionDispatcher Methods
The dispatcher provides execution methods for different transaction types:
Read Operations
dirtyRead(chainId?: ChainId | ChainId[], client?: Client)- Fast read operationdirtyReadAll(client?: Client)- Read from all chainslocal(chainId?: ChainId | ChainId[], client?: Client)- Local executionlocalAll(client?: Client)- Local execution on all chains
Write Operations
submit(chainId?: ChainId | ChainId[], preflight?: boolean, client?: Client)- Submit transactionsubmitAll(preflight?: boolean, client?: Client)- Submit to all chainssubmitAndListen(chainId?: ChainId | ChainId[], preflight?: boolean, client?: Client)- Submit and wait for resultsubmitAndListenAll(preflight?: boolean, client?: Client)- Submit and listen on all chains
Utility
getSignedTransaction()- Get the signed transaction object
Error Handling
try {
const result = await execution('(coin.transfer "alice" "bob" 10.0)')
.withChainId("0")
.sign(wallet)
.submitAndListen();
console.log("Success:", result);
} catch (error) {
if (error.message.includes("No wallet provided")) {
console.error("Please provide a wallet");
} else if (error.message.includes("No client provided")) {
console.error("Network configuration issue");
} else {
console.error("Transaction error:", error);
}
}Best Practices
1. Always Specify Chain ID
// Always set chain ID explicitly
const tx = execution("...")
.withChainId("0"); // Explicit chain ID2. Use Appropriate Execution Method
// Use dirtyRead for simple queries
const balance = await execution('(coin.get-balance "alice")')
.build()
.dirtyRead();
// Use local for complex queries or validation
const estimate = await execution('(coin.transfer "alice" "bob" 10.0)')
.withSigner("alice-key", (signFor) => [signFor("coin.TRANSFER", "alice", "bob", 10.0)])
.build()
.local();
// Use submitAndListen for write operations
const result = await execution('(coin.transfer "alice" "bob" 10.0)')
.sign(wallet)
.submitAndListen();3. Handle Network Context Properly
import { getToolboxGlobalMultiNetworkConfig, validateNetworkForEnvironment } from "@pact-toolbox/transaction";
// Check network availability
const config = getToolboxGlobalMultiNetworkConfig();
if (!validateNetworkForEnvironment(config.default)) {
throw new Error("Current network not available in this environment");
}4. Provide Appropriate Capabilities
const tx = execution('(coin.transfer "alice" "bob" 10.0)')
.withSigner("alice-key", (signFor) => [
signFor("coin.TRANSFER", "alice", "bob", 10.0), // Specific transfer capability
signFor("coin.GAS") // Gas capability
]);Examples
Complete Transfer Example
import { execution } from "@pact-toolbox/transaction";
async function transferTokens(fromAccount: string, toAccount: string, amount: number, wallet: any) {
try {
const result = await execution(`(coin.transfer "${fromAccount}" "${toAccount}" ${amount})`)
.withSigner(wallet.publicKey, (signFor) => [
signFor("coin.TRANSFER", fromAccount, toAccount, amount),
signFor("coin.GAS")
])
.withChainId("0")
.withMeta({
gasLimit: 1000,
gasPrice: 0.00001,
ttl: 600,
})
.sign(wallet)
.submitAndListen();
console.log("Transfer successful:", result);
return result;
} catch (error) {
console.error("Transfer failed:", error);
throw error;
}
}Query Balance Example
import { execution } from "@pact-toolbox/transaction";
async function getBalance(account: string, chainId: string = "0") {
const result = await execution(`(coin.get-balance "${account}")`)
.withChainId(chainId)
.build()
.dirtyRead();
return result;
}Module Deployment Example
import { execution } from "@pact-toolbox/transaction";
async function deployModule(moduleCode: string, adminKeyset: any, wallet: any) {
const result = await execution(moduleCode)
.withKeyset("module-admin", adminKeyset)
.withSigner(wallet.publicKey, (signFor) => [
signFor("coin.GAS")
])
.withChainId("0")
.withMeta({
gasLimit: 100000,
gasPrice: 0.00001,
})
.sign(wallet)
.submitAndListen();
return result;
}License
MIT
Made with ❤️ by @salamaashoush
