From c8cfafd8d916e1f28fe5fed241a0ee690110916a Mon Sep 17 00:00:00 2001 From: Arno Simon Date: Tue, 24 Sep 2024 15:32:59 +0200 Subject: [PATCH] add eth raw signing (#124) * add eth raw signing * fix types export and enable esModuleInterop * format * update examples --- examples/.env.example | 6 ++- examples/eth.ts | 52 +++++++++++++++++++ examples/near.ts | 72 ++++++++++++-------------- src/api.ts | 12 ----- src/fireblocks.ts | 46 ++++++++++++++++ src/kiln.ts | 4 +- src/openapi/{schema.d.ts => schema.ts} | 0 tsconfig.json | 2 +- 8 files changed, 139 insertions(+), 55 deletions(-) create mode 100644 examples/eth.ts delete mode 100644 src/api.ts rename src/openapi/{schema.d.ts => schema.ts} (100%) diff --git a/examples/.env.example b/examples/.env.example index 08e701b..ea4bdea 100644 --- a/examples/.env.example +++ b/examples/.env.example @@ -1,2 +1,4 @@ -KILN_API_KEY= -FIREBLOCKS_API_KEY= \ No newline at end of file +KILN_API_URL="" +KILN_ACCOUNT_ID="" +KILN_API_KEY="" +FIREBLOCKS_API_KEY="" \ No newline at end of file diff --git a/examples/eth.ts b/examples/eth.ts new file mode 100644 index 0000000..165e810 --- /dev/null +++ b/examples/eth.ts @@ -0,0 +1,52 @@ +import { Kiln } from "../src/kiln"; +import type { Integration } from "../lib/types/integrations"; +import fs from "node:fs"; +import 'dotenv/config' + + +const apiSecret = fs.readFileSync(`${__dirname}/fireblocks_secret.key`, 'utf8'); + +const k = new Kiln({ + baseUrl: process.env.KILN_API_URL, + apiToken: process.env.KILN_API_KEY, +}); + +const vault: Integration = { + provider: 'fireblocks', + fireblocksApiKey: process.env.FIREBLOCKS_API_KEY, + fireblocksSecretKey: apiSecret, + vaultId: 14, + fireblocksDestinationId: '07df91b4-7788-4833-a8f4-428facef68cc', +}; + +try { + console.log('crafting...'); + const tx = await k.client.POST( + '/v1/eth/transaction/stake', + { + body: { + account_id: process.env.KILN_ACCOUNT_ID, + wallet: '0x91CcA1b774350232391d337213C0dE544DF1Ed75', + amount_wei: '32000000000000000000' + } + } + ); + // Sign and broadcast using Fireblocks contract calls + // console.log('signing and broadcasting...'); + // const res = await k.fireblocks.signAndBroadcastEthTx(vault, tx.data.data, 'ETH_TEST6'); + // console.log(res); + + // Sign and broadcast using Fireblocks raw signing and Kiln Connect to broadcast + console.log('signing...'); + const signResponse = await k.fireblocks.signEthTx(vault, tx.data.data, "ETH_TEST6"); + // console.log('broadcasting...'); + // const broadcastedTx = await k.client.POST("/v1/eth/transaction/broadcast", { + // body: { + // tx_serialized: signResponse.signed_tx.data.signed_tx_serialized, + // } + // }); + // console.log(broadcastedTx); + +} catch (err) { + console.log(err); +} \ No newline at end of file diff --git a/examples/near.ts b/examples/near.ts index c1b8409..f90fcfd 100644 --- a/examples/near.ts +++ b/examples/near.ts @@ -4,47 +4,43 @@ import fs from "node:fs"; import 'dotenv/config' -const apiSecret = fs.readFileSync(__dirname + '/fireblocks_secret.key', 'utf8'); +const apiSecret = fs.readFileSync(`${__dirname}/fireblocks_secret.key`, 'utf8'); -const f = async () => { - const k = new Kiln({ - baseUrl: 'https://api.testnet.kiln.fi', - apiToken: process.env.KILN_API_KEY, - }); +const k = new Kiln({ + baseUrl: 'https://api.testnet.kiln.fi', + apiToken: process.env.KILN_API_KEY, +}); - const vault: Integration = { - provider: 'fireblocks', - fireblocksApiKey: process.env.FIREBLOCKS_API_KEY, - fireblocksSecretKey: apiSecret, - vaultId: 14 - }; +const vault: Integration = { + provider: 'fireblocks', + fireblocksApiKey: process.env.FIREBLOCKS_API_KEY, + fireblocksSecretKey: apiSecret, + vaultId: 14 +}; - try { - console.log('crafting...'); - const tx = await k.client.POST( - '/v1/near/transaction/stake', - { - body: { - account_id: 'd3f1b917-72b1-4982-a4dd-93fce579a708', - wallet: 'c36b1a5da2e60d1fd5d3a6b46f7399eb26571457f3272f3c978bc9527ad2335f', - pool_id: 'kiln.pool.f863973.m0', - amount_yocto: '1000000000000000000000000', - } - } - ); - console.log('signing...'); - const signResponse = await k.fireblocks.signNearTx(vault, tx.data.data, "NEAR_TEST"); - console.log('broadcasting...'); - const broadcastedTx = await k.client.POST("/v1/near/transaction/broadcast", { +try { + console.log('crafting...'); + const tx = await k.client.POST( + '/v1/near/transaction/stake', + { body: { - signed_tx_serialized: signResponse.signed_tx.data.signed_tx_serialized, + account_id: 'd3f1b917-72b1-4982-a4dd-93fce579a708', + wallet: 'c36b1a5da2e60d1fd5d3a6b46f7399eb26571457f3272f3c978bc9527ad2335f', + pool_id: 'kiln.pool.f863973.m0', + amount_yocto: '1000000000000000000000000', } - }); - console.log(broadcastedTx); - - } catch (err) { - console.log(err); - } -}; + } + ); + console.log('signing...'); + const signResponse = await k.fireblocks.signNearTx(vault, tx.data.data, "NEAR_TEST"); + console.log('broadcasting...'); + const broadcastedTx = await k.client.POST("/v1/near/transaction/broadcast", { + body: { + signed_tx_serialized: signResponse.signed_tx.data.signed_tx_serialized, + } + }); + console.log(broadcastedTx); -f(); \ No newline at end of file +} catch (err) { + console.log(err); +} \ No newline at end of file diff --git a/src/api.ts b/src/api.ts deleted file mode 100644 index ade7516..0000000 --- a/src/api.ts +++ /dev/null @@ -1,12 +0,0 @@ -import axios from 'axios'; - -const api = axios.create(); -api.interceptors.response.use( - (res) => res, - (err) => { - console.log(err); - throw new Error(err.response.data.message); - }, -); - -export default api; diff --git a/src/fireblocks.ts b/src/fireblocks.ts index fc5e049..9d3ee15 100644 --- a/src/fireblocks.ts +++ b/src/fireblocks.ts @@ -619,6 +619,52 @@ export class FireblocksService { }; } + /** + * Sign and broadcast an ETH transaction with given integration using Fireblocks contract call feature + * @param integration + * @param tx + * @param assetId + * @param note + */ + async signEthTx( + integration: FireblocksIntegration, + tx: components['schemas']['ETHUnsignedTx'], + assetId: 'ETH_TEST6' | 'ETH', + note?: string, + ) { + const payload = { + rawMessageData: { + messages: [ + { + content: tx.unsigned_tx_hash, + preHash: { + content: tx.unsigned_tx_serialized, + hashAlgorithm: 'KECCAK256', + }, + }, + ], + }, + }; + + const fbSigner = this.getSigner(integration); + const fbNote = note ? note : 'ETH tx from @kilnfi/sdk'; + const fbTx = await fbSigner.sign(payload, assetId, fbNote); + + const preparedTx = await this.client.POST('/v1/eth/transaction/prepare', { + body: { + unsigned_tx_serialized: tx.unsigned_tx_serialized, + r: `0x${fbTx?.signedMessages?.[0].signature.r}`, + s: `0x${fbTx?.signedMessages?.[0].signature.s}`, + v: fbTx?.signedMessages?.[0].signature.v ?? 0, + }, + }); + + return { + signed_tx: preparedTx.data, + fireblocks_tx: fbTx, + }; + } + /** * Sign and broadcast an ETH transaction with given integration using Fireblocks contract call feature * @param integration diff --git a/src/kiln.ts b/src/kiln.ts index d3598d4..edaf56e 100644 --- a/src/kiln.ts +++ b/src/kiln.ts @@ -1,9 +1,9 @@ +import type { paths } from './openapi/schema'; export * from './validators'; -export type * from './openapi/schema.d.ts'; +export * from './openapi/schema'; export * from './utils'; import createClient, { type Client } from 'openapi-fetch'; import { FireblocksService } from './fireblocks'; -import type { paths } from './openapi/schema.d.ts'; type Config = { baseUrl: string; diff --git a/src/openapi/schema.d.ts b/src/openapi/schema.ts similarity index 100% rename from src/openapi/schema.d.ts rename to src/openapi/schema.ts diff --git a/tsconfig.json b/tsconfig.json index 70250fa..52e2785 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,7 @@ "declaration": true, "outDir": "./lib", "strict": true, - "esModuleInterop": false, + "esModuleInterop": true, "allowSyntheticDefaultImports": true, "forceConsistentCasingInFileNames": true, "verbatimModuleSyntax": false,