|
| 1 | +import { fromBech32, toBech32 } from "@interchainjs/encoding"; |
| 2 | +import { Decimal, Uint53 } from "@interchainjs/math"; |
| 3 | +import { Secp256k1HDWallet } from "@interchainjs/cosmos/wallets/secp256k1hd"; |
| 4 | +import { GasPrice, SigningStargateClient, calculateFee, parseCoins, } from "@cosmjs/stargate"; |
| 5 | +import type { StdFee } from "@interchainjs/cosmos"; |
| 6 | +import type { MsgSend } from "interchainjs/cosmos/bank/v1beta1/tx"; |
| 7 | +// ---- Types and Interfaces ---- |
| 8 | +interface TransactionConfig { |
| 9 | + rpcEndpoint: string; |
| 10 | + gasPrice: string; |
| 11 | + gasLimit: number; |
| 12 | + memo?: string; |
| 13 | +} |
| 14 | +interface SendTokensParams { |
| 15 | + recipientAddress: string; |
| 16 | + amount: { |
| 17 | + denom: string; |
| 18 | + amount: string; |
| 19 | + }; |
| 20 | +} |
| 21 | +// ---- Address Utilities ---- |
| 22 | +export function handleBech32Address(address: string): { |
| 23 | + decodedAddress: { |
| 24 | + prefix: string; |
| 25 | + data: Uint8Array; |
| 26 | + }; |
| 27 | + reEncodedAddress: string; |
| 28 | +} { |
| 29 | + const decodedAddress = fromBech32(address); |
| 30 | + const reEncodedAddress = toBech32(decodedAddress.prefix, decodedAddress.data); |
| 31 | + return { |
| 32 | + decodedAddress, |
| 33 | + reEncodedAddress, |
| 34 | + }; |
| 35 | +} |
| 36 | +// ---- Coin Utilities ---- |
| 37 | +export function parseCoinStrings(coinString: string) { |
| 38 | + try { |
| 39 | + const parsed = parseCoins(coinString); |
| 40 | + return { |
| 41 | + success: true, |
| 42 | + coins: parsed, |
| 43 | + total: parsed.reduce((acc, coin) => acc + Number(coin.amount), 0), |
| 44 | + }; |
| 45 | + } |
| 46 | + catch (error) { |
| 47 | + return { |
| 48 | + success: false, |
| 49 | + error: `Failed to parse coin string: ${error.message}`, |
| 50 | + coins: [], |
| 51 | + total: 0, |
| 52 | + }; |
| 53 | + } |
| 54 | +} |
| 55 | +// ---- Decimal Calculation Utilities ---- |
| 56 | +export namespace DecimalCalculator { |
| 57 | + export function add(amount1: string, amount2: string, precision = 6): string { |
| 58 | + const dec1 = Decimal.fromUserInput(amount1, precision); |
| 59 | + const dec2 = Decimal.fromUserInput(amount2, precision); |
| 60 | + return dec1.plus(dec2).toString(); |
| 61 | + } |
| 62 | + export function multiply(amount: string, multiplier: number, precision = 6): string { |
| 63 | + const dec = Decimal.fromUserInput(amount, precision); |
| 64 | + return dec.multiply(new Uint53(multiplier)).toString(); |
| 65 | + } |
| 66 | + export function divide(amount: string, divisor: number, precision = 6): number { |
| 67 | + const dec = Decimal.fromUserInput(amount, precision); |
| 68 | + return dec.toFloatApproximation() / divisor; |
| 69 | + } |
| 70 | +} |
| 71 | +// ---- Transaction Management ---- |
| 72 | +export class CosmosTransactionManager { |
| 73 | + private config: TransactionConfig; |
| 74 | + private signer?: DirectSecp256k1HdWallet; |
| 75 | + private client?: SigningStargateClient; |
| 76 | + constructor(config: TransactionConfig) { |
| 77 | + this.config = { |
| 78 | + ...config, |
| 79 | + memo: config.memo || "Transaction via CosmJS", |
| 80 | + }; |
| 81 | + } |
| 82 | + async initialize(mnemonic: string, prefix = "cosmos") { |
| 83 | + try { |
| 84 | + this.signer = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { |
| 85 | + prefix, |
| 86 | + }); |
| 87 | + this.client = await SigningClient.connectWithSigner(this.config.rpcEndpoint, this.signer, { |
| 88 | + broadcast: { checkTx: true, deliverTx: true } |
| 89 | + }); |
| 90 | + return true; |
| 91 | + } |
| 92 | + catch (error) { |
| 93 | + console.error("Failed to initialize transaction manager:", error); |
| 94 | + throw new Error(`Initialization failed: ${error.message}`); |
| 95 | + } |
| 96 | + } |
| 97 | + private calculateTransactionFee(): StdFee { |
| 98 | + const gasPrice = GasPrice.fromString(this.config.gasPrice); |
| 99 | + return calculateFee(this.config.gasLimit, gasPrice); |
| 100 | + } |
| 101 | + async sendTokens(params: SendTokensParams) { |
| 102 | + if (!this.signer || !this.client) { |
| 103 | + throw new Error("Transaction manager not initialized"); |
| 104 | + } |
| 105 | + try { |
| 106 | + const [account] = await this.signer.getAccounts(); |
| 107 | + const senderAddress = account.address; |
| 108 | + const message: MsgSend = { |
| 109 | + fromAddress: senderAddress, |
| 110 | + toAddress: params.recipientAddress, |
| 111 | + amount: [params.amount], |
| 112 | + }; |
| 113 | + const msgAny = { |
| 114 | + typeUrl: "/cosmos.bank.v1beta1.MsgSend", |
| 115 | + value: message, |
| 116 | + }; |
| 117 | + const fee = this.calculateTransactionFee(); |
| 118 | + const result = await this.client.signAndBroadcast(senderAddress, [msgAny], fee, this.config.memo); |
| 119 | + return { |
| 120 | + success: true, |
| 121 | + hash: result.transactionHash, |
| 122 | + details: result, |
| 123 | + }; |
| 124 | + } |
| 125 | + catch (error) { |
| 126 | + return { |
| 127 | + success: false, |
| 128 | + error: error.message, |
| 129 | + }; |
| 130 | + } |
| 131 | + } |
| 132 | +} |
| 133 | +// ---- Example Usage ---- |
| 134 | +async function demonstrateFeatures() { |
| 135 | + // Address handling example |
| 136 | + const addressResult = handleBech32Address("cosmos1zmj0fpkm9px3f7klg7hzycu6z6k76jskvnsc0n"); |
| 137 | + console.log("Address handling result:", addressResult); |
| 138 | + // Coin parsing example |
| 139 | + const coinResult = parseCoinStrings("1000000uatom,500000stake"); |
| 140 | + console.log("Coin parsing result:", coinResult); |
| 141 | + // Decimal calculations |
| 142 | + const sum = DecimalCalculator.add("123.456", "0.001"); |
| 143 | + const product = DecimalCalculator.multiply("123.456", 2); |
| 144 | + const quotient = DecimalCalculator.divide("123.456", 10); |
| 145 | + console.log("Calculations:", { sum, product, quotient }); |
| 146 | + // Transaction example |
| 147 | + const manager = new CosmosTransactionManager({ |
| 148 | + rpcEndpoint: "rpc.cosmos.network:26657", |
| 149 | + gasPrice: "0.025uatom", |
| 150 | + gasLimit: 200000, |
| 151 | + }); |
| 152 | + try { |
| 153 | + await manager.initialize("your-mnemonic-here"); |
| 154 | + const result = await manager.sendTokens({ |
| 155 | + recipientAddress: "cosmos1...", |
| 156 | + amount: { |
| 157 | + denom: "uatom", |
| 158 | + amount: "1000", |
| 159 | + }, |
| 160 | + }); |
| 161 | + console.log("Transaction result:", result); |
| 162 | + } |
| 163 | + catch (error) { |
| 164 | + console.error("Transaction failed:", error); |
| 165 | + } |
| 166 | +} |
| 167 | +// Uncomment to run all examples: |
| 168 | +// demonstrateFeatures(); |
0 commit comments