diff --git a/bun.lockb b/bun.lockb index 0f86480..70faf29 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index a810e83..7e7c9dd 100644 --- a/package.json +++ b/package.json @@ -34,9 +34,9 @@ }, "homepage": "https://github.com/kilnfi/sdk-js#readme", "dependencies": { + "@fireblocks/ts-sdk": "^6.0.0", "@types/bun": "^1.1.11", "bech32": "^2.0.0", - "fireblocks-sdk": "^5.32.0", "openapi-fetch": "^0.12.0", "viem": "^2.21.29" }, diff --git a/src/fireblocks.ts b/src/fireblocks.ts index 4a12430..88046ec 100644 --- a/src/fireblocks.ts +++ b/src/fireblocks.ts @@ -1,26 +1,21 @@ import { type AssetTypeResponse, - FireblocksSDK, - type PublicKeyResponse, - type SDKOptions, - SigningAlgorithm, + type ConfigurationOptions, + Fireblocks, + type PublicKeyInformation, + SignedMessageAlgorithmEnum, type TransactionResponse, -} from 'fireblocks-sdk'; -import type { IAuthProvider } from 'fireblocks-sdk/dist/src/iauth-provider.js'; +} from '@fireblocks/ts-sdk'; + import type { Client } from 'openapi-fetch'; import { FireblocksSigner } from './fireblocks_signer.js'; import type { components, paths } from './openapi/schema.js'; -export type FireblocksIntegration = { - provider: 'fireblocks'; - fireblocksApiKey: string; - fireblocksSecretKey: string; - vaultId: number; - name?: string; - fireblocksDestinationId?: string; - fireblocksApiBaseUrl?: string; - fireblocksAuthProvider?: IAuthProvider; - fireblocksSdkOptions?: SDKOptions; +export type FireblocksIntegration = ( + | { config?: never; instance: Fireblocks } + | { config: ConfigurationOptions; instance?: never } +) & { + vaultId: `${number}`; }; export class FireblocksService { @@ -33,14 +28,11 @@ export class FireblocksService { /** * Retrieve a fireblocks SDK from a Fireblocks integration */ - getSdk(integration: FireblocksIntegration): FireblocksSDK { - return new FireblocksSDK( - integration.fireblocksSecretKey, - integration.fireblocksApiKey, - integration.fireblocksApiBaseUrl, - integration.fireblocksAuthProvider, - integration.fireblocksSdkOptions, - ); + getSdk(integration: FireblocksIntegration): Fireblocks { + if (integration.instance) { + return integration.instance; + } + return new Fireblocks(integration.config); } /** @@ -54,16 +46,16 @@ export class FireblocksService { /** * Get fireblocks wallet pubkey compressed */ - async getPubkey(integration: FireblocksIntegration, assetId: string): Promise { + async getPubkey(integration: FireblocksIntegration, assetId: string): Promise { const fbSdk = this.getSdk(integration); - const data = await fbSdk.getPublicKeyInfoForVaultAccount({ + const data = await fbSdk.vaults.getPublicKeyInfoForAddress({ assetId: assetId, vaultAccountId: integration.vaultId, change: 0, addressIndex: 0, compressed: true, }); - return data; + return data.data; } /** @@ -71,7 +63,7 @@ export class FireblocksService { */ async getAssets(integration: FireblocksIntegration): Promise { const fbSdk = this.getSdk(integration); - return await fbSdk.getSupportedAssets(); + return (await fbSdk.blockchainsAssets.getSupportedAssets()).data; } /** @@ -101,8 +93,9 @@ export class FireblocksService { const fbTx = await fbSigner.sign(payload, assetId, fbNote); const signatures = fbTx.signedMessages - ?.filter((signedMessage) => signedMessage.derivationPath[3] === 0) - .map((signedMessage) => signedMessage.signature.fullSig); + ?.filter((signedMessage) => signedMessage.derivationPath?.[3] === 0) + .map((signedMessage) => signedMessage.signature?.fullSig) + .filter((s) => s !== undefined); if (!signatures) { throw new Error('Fireblocks signature is missing'); } @@ -156,12 +149,10 @@ export class FireblocksService { const fbNote = note ? note : 'ADA tx from @kilnfi/sdk'; const fbTx = await fbSigner.sign(payload, 'ADA', fbNote); - const signedMessages = fbTx.signedMessages?.map((message) => { - return { - pubkey: message.publicKey, - signature: message.signature.fullSig, - }; - }); + const signedMessages = fbTx.signedMessages?.map((message) => ({ + pubkey: message.publicKey as string, + signature: message.signature?.fullSig as string, + })); if (!signedMessages) { throw new Error('Fireblocks signature is missing'); } @@ -211,7 +202,7 @@ export class FireblocksService { const fbSigner = this.getSigner(integration); const fbNote = note ? note : 'ATOM tx from @kilnfi/sdk'; const fbTx = await fbSigner.sign(payload, 'ATOM_COS', fbNote); - const signature = fbTx.signedMessages?.[0]?.signature.fullSig; + const signature = fbTx.signedMessages?.[0]?.signature?.fullSig; if (!signature) { throw new Error('Fireblocks signature is missing'); @@ -264,7 +255,7 @@ export class FireblocksService { const fbSigner = this.getSigner(integration); const fbNote = note ? note : 'DYDX tx from @kilnfi/sdk'; const fbTx = await fbSigner.sign(payload, 'DYDX_DYDX', fbNote); - const signature = fbTx.signedMessages?.[0]?.signature.fullSig; + const signature = fbTx.signedMessages?.[0]?.signature?.fullSig; if (!signature) { throw new Error('Fireblocks signature is missing'); @@ -312,14 +303,14 @@ export class FireblocksService { }, }, ], - algorithm: SigningAlgorithm.MPC_ECDSA_SECP256K1, + algorithm: SignedMessageAlgorithmEnum.EcdsaSecp256K1, }, }; const fbSigner = this.getSigner(integration); const fbNote = note ? note : 'FET tx from @kilnfi/sdk'; const fbTx = await fbSigner.sign(payload, undefined, fbNote); - const signature = fbTx.signedMessages?.[0]?.signature.fullSig; + const signature = fbTx.signedMessages?.[0]?.signature?.fullSig; if (!signature) { throw new Error('Fireblocks signature is missing'); @@ -367,14 +358,14 @@ export class FireblocksService { }, }, ], - algorithm: SigningAlgorithm.MPC_ECDSA_SECP256K1, + algorithm: SignedMessageAlgorithmEnum.EcdsaSecp256K1, }, }; const fbSigner = this.getSigner(integration); const fbNote = note ? note : 'OM tx from @kilnfi/sdk'; const fbTx = await fbSigner.sign(payload, undefined, fbNote); - const signature = fbTx.signedMessages?.[0]?.signature.fullSig; + const signature = fbTx.signedMessages?.[0]?.signature?.fullSig; if (!signature) { throw new Error('Fireblocks signature is missing'); @@ -427,7 +418,7 @@ export class FireblocksService { const fbSigner = this.getSigner(integration); const fbNote = note ? note : 'INJ tx from @kilnfi/sdk'; const fbTx = await fbSigner.sign(payload, 'INJ_INJ', fbNote); - const signature = fbTx.signedMessages?.[0]?.signature.fullSig; + const signature = fbTx.signedMessages?.[0]?.signature?.fullSig; if (!signature) { throw new Error('Fireblocks signature is missing'); @@ -475,14 +466,14 @@ export class FireblocksService { }, }, ], - algorithm: SigningAlgorithm.MPC_ECDSA_SECP256K1, + algorithm: SignedMessageAlgorithmEnum.EcdsaSecp256K1, }, }; const fbSigner = this.getSigner(integration); const fbNote = note ? note : 'KAVA tx from @kilnfi/sdk'; const fbTx = await fbSigner.sign(payload, undefined, fbNote); - const signature = fbTx.signedMessages?.[0]?.signature.fullSig; + const signature = fbTx.signedMessages?.[0]?.signature?.fullSig; if (!signature) { throw new Error('Fireblocks signature is missing'); @@ -530,14 +521,14 @@ export class FireblocksService { }, }, ], - algorithm: SigningAlgorithm.MPC_ECDSA_SECP256K1, + algorithm: SignedMessageAlgorithmEnum.EcdsaSecp256K1, }, }; const fbSigner = this.getSigner(integration); const fbNote = note ? note : 'NOBLE tx from @kilnfi/sdk'; const fbTx = await fbSigner.sign(payload, undefined, fbNote); - const signature = fbTx.signedMessages?.[0]?.signature.fullSig; + const signature = fbTx.signedMessages?.[0]?.signature?.fullSig; if (!signature) { throw new Error('Fireblocks signature is missing'); @@ -590,7 +581,7 @@ export class FireblocksService { const fbSigner = this.getSigner(integration); const fbNote = note ? note : 'OSMO tx from @kilnfi/sdk'; const fbTx = await fbSigner.sign(payload, 'OSMO', fbNote); - const signature = fbTx.signedMessages?.[0]?.signature.fullSig; + const signature = fbTx.signedMessages?.[0]?.signature?.fullSig; if (!signature) { throw new Error('Fireblocks signature is missing'); @@ -643,7 +634,7 @@ export class FireblocksService { const fbSigner = this.getSigner(integration); const fbNote = note ? note : 'TIA tx from @kilnfi/sdk'; const fbTx = await fbSigner.sign(payload, 'CELESTIA', fbNote); - const signature = fbTx.signedMessages?.[0]?.signature.fullSig; + const signature = fbTx.signedMessages?.[0]?.signature?.fullSig; if (!signature) { throw new Error('Fireblocks signature is missing'); @@ -691,14 +682,14 @@ export class FireblocksService { }, }, ], - algorithm: SigningAlgorithm.MPC_ECDSA_SECP256K1, + algorithm: SignedMessageAlgorithmEnum.EcdsaSecp256K1, }, }; const fbSigner = this.getSigner(integration); const fbNote = note ? note : 'ZETA tx from @kilnfi/sdk'; const fbTx = await fbSigner.sign(payload, undefined, fbNote); - const signature = fbTx.signedMessages?.[0]?.signature.fullSig; + const signature = fbTx.signedMessages?.[0]?.signature?.fullSig; if (!signature) { throw new Error('Fireblocks signature is missing'); @@ -747,6 +738,11 @@ export class FireblocksService { const fbSigner = this.getSigner(integration); const fbNote = note ? note : 'DOT tx from @kilnfi/sdk'; const fbTx = await fbSigner.sign(payload, 'DOT', fbNote); + + if (!fbTx.signedMessages?.[0]?.signature?.fullSig) { + throw new Error('Fireblocks signature is missing'); + } + const signature = `0x00${fbTx.signedMessages?.[0]?.signature.fullSig}`; const preparedTx = await this.client.POST('/dot/transaction/prepare', { @@ -790,6 +786,11 @@ export class FireblocksService { const fbSigner = this.getSigner(integration); const fbNote = note ? note : 'KSM tx from @kilnfi/sdk'; const fbTx = await fbSigner.sign(payload, 'KSM', fbNote); + + if (!fbTx.signedMessages?.[0]?.signature?.fullSig) { + throw new Error('Fireblocks signature is missing'); + } + const signature = `0x00${fbTx.signedMessages?.[0]?.signature.fullSig}`; const preparedTx = await this.client.POST('/ksm/transaction/prepare', { @@ -871,18 +872,16 @@ export class FireblocksService { integration: FireblocksIntegration, tx: components['schemas']['ETHUnsignedTx'], assetId: 'ETH_TEST6' | 'ETH', + fireblocksDestinationId: string, note?: string, ) { - if (!integration.fireblocksDestinationId) { - throw new Error('Fireblocks destination id is missing'); - } const payload = { contractCallData: tx.contract_call_data, }; const fbSigner = this.getSigner(integration); const fbNote = note ? note : 'ETH tx from @kilnfi/sdk'; - return await fbSigner.signAndBroadcastWith(payload, assetId, tx, integration.fireblocksDestinationId, true, fbNote); + return await fbSigner.signAndBroadcastWith(payload, assetId, tx, fireblocksDestinationId, true, fbNote); } /** @@ -947,18 +946,16 @@ export class FireblocksService { integration: FireblocksIntegration, tx: components['schemas']['POLUnsignedTx'], assetId: 'ETH_TEST5' | 'ETH', + fireblocksDestinationId: string, note?: string, ) { - if (!integration.fireblocksDestinationId) { - throw new Error('Fireblocks destination id is missing'); - } const payload = { contractCallData: tx.contract_call_data, }; const fbSigner = this.getSigner(integration); const fbNote = note ? note : 'POL tx from @kilnfi/sdk'; - return await fbSigner.signAndBroadcastWith(payload, assetId, tx, integration.fireblocksDestinationId, true, fbNote); + return await fbSigner.signAndBroadcastWith(payload, assetId, tx, fireblocksDestinationId, true, fbNote); } /** @@ -986,7 +983,7 @@ export class FireblocksService { const fbSigner = this.getSigner(integration); const fbNote = note ? note : 'TON tx from @kilnfi/sdk'; const fbTx = await fbSigner.sign(payload, assetId, fbNote); - const signature = fbTx.signedMessages?.[0]?.signature.fullSig; + const signature = fbTx.signedMessages?.[0]?.signature?.fullSig; if (!signature) { throw new Error('Fireblocks signature is missing'); @@ -1035,7 +1032,7 @@ export class FireblocksService { const fbSigner = this.getSigner(integration); const fbNote = note ? note : 'XTZ tx from @kilnfi/sdk'; const fbTx = await fbSigner.sign(payload, assetId, fbNote); - const signature = fbTx.signedMessages?.[0]?.signature.fullSig; + const signature = fbTx.signedMessages?.[0]?.signature?.fullSig; if (!signature) { throw new Error('Fireblocks signature is missing'); @@ -1083,7 +1080,7 @@ export class FireblocksService { const fbSigner = this.getSigner(integration); const fbNote = note ? note : 'NEAR tx from @kilnfi/sdk'; const fbTx = await fbSigner.sign(payload, assetId, fbNote); - const signature = fbTx.signedMessages?.[0]?.signature.fullSig; + const signature = fbTx.signedMessages?.[0]?.signature?.fullSig; if (!signature) { throw new Error('Fireblocks signature is missing'); diff --git a/src/fireblocks_signer.ts b/src/fireblocks_signer.ts index c1b8140..ff3179c 100644 --- a/src/fireblocks_signer.ts +++ b/src/fireblocks_signer.ts @@ -1,13 +1,9 @@ -import { - type CreateTransactionResponse, - type FireblocksSDK, - PeerType, - type TransactionArguments, - TransactionOperation, - type TransactionResponse, - TransactionStatus, -} from 'fireblocks-sdk'; - +import type { + CreateTransactionResponse, + Fireblocks, + TransactionRequest, + TransactionResponse, +} from '@fireblocks/ts-sdk'; import { formatEther, formatUnits } from 'viem'; import type { components } from './openapi/schema.js'; @@ -43,13 +39,10 @@ export type FireblocksAssetId = | 'BTC'; export class FireblocksSigner { - protected fireblocks: FireblocksSDK; - protected vaultId: number; - - constructor(fireblocks: FireblocksSDK, vaultId: number) { - this.fireblocks = fireblocks; - this.vaultId = vaultId; - } + constructor( + protected fireblocks: Fireblocks, + protected vaultId: `${number}`, + ) {} /** * Wait for given transaction to be completed @@ -58,23 +51,19 @@ export class FireblocksSigner { protected async waitForTxCompletion(fbTx: CreateTransactionResponse): Promise { try { let tx = fbTx; - while (tx.status !== TransactionStatus.COMPLETED) { - if ( - tx.status === TransactionStatus.BLOCKED || - tx.status === TransactionStatus.FAILED || - tx.status === TransactionStatus.CANCELLED - ) { + while (tx.status !== 'COMPLETED') { + if (tx.status === 'BLOCKED' || tx.status === 'FAILED' || tx.status === 'CANCELLED') { throw Error(`Fireblocks signer: the transaction has been ${tx.status}`); } - if (tx.status === TransactionStatus.REJECTED) { + if (tx.status === 'REJECTED') { throw Error( 'Fireblocks signer: the transaction has been rejected, make sure that the TAP security policy is not blocking the transaction', ); } - tx = await this.fireblocks.getTransactionById(fbTx.id); + tx = (await this.fireblocks.transactions.getTransaction({ txId: fbTx.id as string })).data; } - return await this.fireblocks.getTransactionById(fbTx.id); + return tx; } catch (err) { throw new Error(`Fireblocks signer (waitForTxCompletion): ${err}`); } @@ -89,22 +78,22 @@ export class FireblocksSigner { public async sign(payloadToSign: object, assetId?: FireblocksAssetId, note = ''): Promise { try { const assetArgs = assetId - ? { + ? ({ assetId, source: { - type: PeerType.VAULT_ACCOUNT, - id: this.vaultId.toString(), + type: 'VAULT_ACCOUNT', + id: this.vaultId, }, - } - : {}; + } satisfies Partial) + : undefined; - const tx: TransactionArguments = { + const tx: TransactionRequest = { ...assetArgs, - operation: TransactionOperation.RAW, + operation: 'RAW', note, extraParameters: payloadToSign, }; - const fbTx = await this.fireblocks.createTransaction(tx); + const fbTx = (await this.fireblocks.transactions.createTransaction({ transactionRequest: tx })).data; return await this.waitForTxCompletion(fbTx); } catch (err) { throw new Error(`Fireblocks signer (signWithFB): ${err}`); @@ -123,12 +112,12 @@ export class FireblocksSigner { note = '', ): Promise { try { - const tx: TransactionArguments = { + const tx: TransactionRequest = { assetId: assetId, - operation: TransactionOperation.TYPED_MESSAGE, + operation: 'TYPED_MESSAGE', source: { - type: PeerType.VAULT_ACCOUNT, - id: this.vaultId.toString(), + type: 'VAULT_ACCOUNT', + id: this.vaultId, }, note, extraParameters: { @@ -142,7 +131,7 @@ export class FireblocksSigner { }, }, }; - const fbTx = await this.fireblocks.createTransaction(tx); + const fbTx = (await this.fireblocks.transactions.createTransaction({ transactionRequest: tx })).data; return await this.waitForTxCompletion(fbTx); } catch (err) { throw new Error(`Fireblocks signer (signWithFB): ${err}`); @@ -167,15 +156,15 @@ export class FireblocksSigner { note = '', ): Promise { try { - const txArgs: TransactionArguments = { + const txArgs: TransactionRequest = { assetId: assetId, - operation: TransactionOperation.CONTRACT_CALL, + operation: 'CONTRACT_CALL', source: { - type: PeerType.VAULT_ACCOUNT, - id: this.vaultId.toString(), + type: 'VAULT_ACCOUNT', + id: this.vaultId, }, destination: { - type: PeerType.EXTERNAL_WALLET, + type: 'EXTERNAL_WALLET', id: destinationId, }, amount: tx.amount_wei && sendAmount ? formatEther(BigInt(tx.amount_wei), 'wei') : '0', @@ -185,7 +174,7 @@ export class FireblocksSigner { 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.createTransaction(txArgs); + const fbTx = (await this.fireblocks.transactions.createTransaction({ transactionRequest: txArgs })).data; return await this.waitForTxCompletion(fbTx); } catch (err) { throw new Error(`Fireblocks signer (signAndBroadcastWithFB): ${err}`);