UNPKG

@dolaned/wallet-sdk-ts

Version:

Wallet SDK for the Nexa blockchain

146 lines (123 loc) 6 kB
import {PrivateKey, TransactionBuilder} from "libnexa-ts"; import {rostrumProvider} from "../../network/RostrumProvider"; import { buildCreateGroupTransaction, populateAndDuplicateTokenAuths, populateNexaInputsAndChange, populateTokenAuth, populateTokenInputsAndChange, prepareDeleteTransaction } from "../../utils/TXUtils"; import {BaseAccount} from "../accounts/interfaces/BaseAccountInterface"; import {AddressKey} from "../../models/wallet.entities"; import {TransactionCreator} from "./interfaces/TransactionCreator"; import {keys} from "lodash-es"; import {PermissionLabel} from "../../models/transaction.entities"; export default class WalletTransactionCreator extends TransactionCreator { private _keysToSign: PrivateKey[] = []; private _account!: BaseAccount; constructor(fromAccount: BaseAccount, tx?: TransactionBuilder | string | Buffer) { super(tx) this._account = fromAccount this.validateAccount() } public fromAccount(fromAccount: BaseAccount): this { this._account = fromAccount return this } public parseTxHex(tx: string) { this.builder.push(async () => { const txBuilder = new TransactionBuilder(tx) const newTxBuilder = new TransactionBuilder() const oldInputs = txBuilder.transaction.inputs for (const input of oldInputs) { const utxo = await rostrumProvider.getUtxo(input.outpoint.toString('hex')) newTxBuilder.from({ outpoint: utxo.tx_hash, amount: utxo.amount, scriptPubKey: utxo.scriptpubkey }) const foundKey = this.findPrivateKeyFromAddress(utxo.addresses[0]) if(foundKey) { this._keysToSign.push(foundKey.key.privateKey) } } newTxBuilder.transaction.outputs = txBuilder.transaction.outputs }) return this } public parseTxBuffer(tx: Buffer) { this.builder.push(async () => { this.transactionBuilder = new TransactionBuilder(tx); }) return this } public mint(token: string, amount: string): this { this.builder.push(async() => { let toAddr = this._account.accountKeys.receiveKeys.at(-1)!.address; this.tokenAction(toAddr, amount, token, 'mint') }) return this } public melt(token: string, amount: string): this { this.builder.push(async() => { let toAddr = this._account.accountKeys.receiveKeys.at(-1)!.address; this.tokenAction(toAddr, amount, token, 'melt') }) return this } public populate(): this { this.validateAccount(); this.builder.push(async () => { let tK: PrivateKey[] = [] let nK: PrivateKey[] = [] if(this.tokens.size > 0) { for (const tokenAction of this.tokens) { if(tokenAction.action == 'mint' || tokenAction.action == 'melt') { tK = tK.concat(await populateTokenAuth(this.transactionBuilder, this._account.accountKeys, tokenAction.token!, tokenAction.action)) } else if (tokenAction.action == 'group') { tK = tK.concat(await buildCreateGroupTransaction(this.transactionBuilder, this._account.accountKeys, tokenAction.extraData?.opReturnData!, this.network)) } else if (tokenAction.action == 'subgroup') { tK = tK.concat(await populateTokenAuth(this.transactionBuilder, this._account.accountKeys, tokenAction.parentToken!, 'subgroup', tokenAction.token, this._account.accountKeys.receiveKeys.at(-1)!.address)); } else if(tokenAction.action == 'renew') { tK = tK.concat(await populateAndDuplicateTokenAuths(this.transactionBuilder, this._account.accountKeys, tokenAction.token!, tokenAction.extraData!.perms!, tokenAction.extraData!.address!)) } else if(tokenAction.action == 'delete') { tK = tK.concat(await prepareDeleteTransaction(this.transactionBuilder, this._account.accountKeys, tokenAction.extraData!.outpoint!)) } else { tK = tK.concat(await populateTokenInputsAndChange(this.transactionBuilder, this._account.accountKeys, tokenAction.token!, tokenAction.amount)) } this._keysToSign!.concat(tK) } } nK = nK.concat(await populateNexaInputsAndChange(this.transactionBuilder, this._account.accountKeys, this.totalValue, this.txOptions)) this._keysToSign! = tK.concat(nK) }) return this } public sign(): this { this.builder.push(async () => { const blockHeight = await rostrumProvider.getBlockTip() this.transactionBuilder.lockUntilBlockHeight(blockHeight.height).sign(this._keysToSign!) }) return this } /** * Validates that the account has the necessary keys before performing operations * @throws Error if account or keys are not properly initialized */ private validateAccount(): void { if (!this._account) { throw new Error('Account must be set before performing transactions'); } if (!this._account.accountKeys) { throw new Error('Account keys are not initialized'); } if (!this._account.accountKeys.receiveKeys || this._account.accountKeys.receiveKeys.length === 0) { throw new Error('No receive keys available in account'); } } private findPrivateKeyFromAddress(addr: string): AddressKey | undefined { const keys: AddressKey[] = this._account.accountKeys.receiveKeys.concat(this._account.accountKeys.changeKeys) return keys.find(key => key.address === addr) } }