UNPKG

@kilnfi/sdk

Version:

JavaScript sdk for Kiln API

172 lines (163 loc) 5.22 kB
import type { CreateTransactionResponse, Fireblocks, TransactionRequest, TransactionResponse, } from '@fireblocks/ts-sdk'; import { formatEther, formatUnits } from 'viem'; import type { components } from './openapi/schema.js'; /** * Fireblocks asset id * ref: https://github.com/fireblocks/fireblocks-web3-provider/blob/main/src/constants.ts */ export type FireblocksAssetId = | 'SOL_TEST' | 'SOL' | 'ETH_TEST5' | 'ETH_TEST_HOODI' | 'ETH' | 'ATOM_COS' | 'OSMO_TEST' | 'OSMO' | 'ADA_TEST' | 'ADA' | 'NEAR_TEST' | 'NEAR' | 'XTZ' | 'DOT' | 'KSM' | 'DV4TNT_TEST' | 'DYDX_DYDX' | 'CELESTIA' | 'INJ_INJ' | 'TON_TEST' | 'TON' | 'KAVA_KAVA' | 'TRX' | 'BTC'; export class FireblocksSigner { constructor( protected fireblocks: Fireblocks, protected vaultId: `${number}`, ) {} /** * Wait for given transaction to be completed * @param fbTx fireblocks transaction */ protected async waitForTxCompletion(fbTx: CreateTransactionResponse): Promise<TransactionResponse> { let tx = fbTx; while (tx.status !== 'COMPLETED') { // see https://developers.fireblocks.com/reference/transaction-substatuses#failed-substatuses const ERRORS: Record<string, string> = { BLOCKED: 'The transaction has been blocked by the TAP security policy.', FAILED: 'The transaction has failed.', CANCELLED: 'The transaction has been cancelled.', REJECTED: 'The transaction has been rejected, make sure that the TAP security policy is not blocking the transaction.', }; if (tx.status && tx.status in ERRORS) { throw Error(ERRORS[tx.status]); } // wait a bit before polling again await new Promise((r) => setTimeout(r, 500)); tx = (await this.fireblocks.transactions.getTransaction({ txId: fbTx.id as string })).data; } return tx; } /** * Sign a transaction with fireblocks using Fireblocks raw message signing feature * @param payloadToSign transaction data in hexadecimal * @param assetId fireblocks asset id * @param note optional fireblocks custom note */ public async sign(payloadToSign: object, assetId?: FireblocksAssetId, note = ''): Promise<TransactionResponse> { const assetArgs = assetId ? ({ assetId, source: { type: 'VAULT_ACCOUNT', id: this.vaultId, }, } satisfies Partial<TransactionRequest>) : undefined; const tx: TransactionRequest = { ...assetArgs, operation: 'RAW', note, extraParameters: payloadToSign, }; const fbTx = (await this.fireblocks.transactions.createTransaction({ transactionRequest: tx })).data; return await this.waitForTxCompletion(fbTx); } /** * Sign an EIP-712 Ethereum typed message with fireblocks * @param eip712message eip712message to sign * @param assetId fireblocks asset id * @param note optional fireblocks custom note */ public async signTypedMessage( eip712message: object, assetId: 'ETH' | 'ETH_TEST5' | 'ETH_TEST_HOODI', note = '', ): Promise<TransactionResponse> { const tx: TransactionRequest = { assetId: assetId, operation: 'TYPED_MESSAGE', source: { type: 'VAULT_ACCOUNT', id: this.vaultId, }, note, extraParameters: { rawMessageData: { messages: [ { content: eip712message, type: 'EIP712', }, ], }, }, }; const fbTx = (await this.fireblocks.transactions.createTransaction({ transactionRequest: tx })).data; return await this.waitForTxCompletion(fbTx); } /** * Sign and broadcast a transaction with fireblocks using Fireblocks contract call feature * @param payloadToSign transaction data in hexadecimal * @param assetId fireblocks asset id * @param note optional fireblocks custom note * @param tx Ethereum transaction * @param destinationId Fireblocks destination id, this corresponds to the Fireblocks whitelisted contract address id * @param sendAmount send the amount in tx to smart contract */ public async signAndBroadcastWith( payloadToSign: object, assetId: FireblocksAssetId, tx: components['schemas']['ETHUnsignedTx'] | components['schemas']['POLUnsignedTx'], destinationId: string, sendAmount = true, note = '', ): Promise<TransactionResponse> { const txArgs: TransactionRequest = { assetId: assetId, operation: 'CONTRACT_CALL', source: { type: 'VAULT_ACCOUNT', id: this.vaultId, }, destination: { type: 'EXTERNAL_WALLET', id: destinationId, }, amount: tx.amount_wei && sendAmount ? formatEther(BigInt(tx.amount_wei), 'wei') : '0', note, extraParameters: payloadToSign, gasLimit: tx.gas_limit, priorityFee: formatUnits(BigInt(tx.max_priority_fee_per_gas_wei), 9), maxFee: formatUnits(BigInt(tx.max_fee_per_gas_wei), 9), }; const fbTx = (await this.fireblocks.transactions.createTransaction({ transactionRequest: txArgs })).data; return await this.waitForTxCompletion(fbTx); } }