From 9083e48c7350e22bd49f8599935167204df01589 Mon Sep 17 00:00:00 2001 From: ilies Date: Mon, 23 Dec 2024 16:06:22 +0100 Subject: [PATCH 1/5] mantra sdk example and sign fx --- examples/mantra.ts | 49 +++++++++++++++++++++++++++++++++++++++ src/fireblocks.ts | 58 ++++++++++++++++++++++++++++++++++++++++++++++ src/utils.ts | 14 +++++++++++ 3 files changed, 121 insertions(+) create mode 100644 examples/mantra.ts diff --git a/examples/mantra.ts b/examples/mantra.ts new file mode 100644 index 0000000..5c876b0 --- /dev/null +++ b/examples/mantra.ts @@ -0,0 +1,49 @@ +import { omToUom, Kiln } from '../src/kiln.ts'; +import fs from 'node:fs'; +import 'dotenv/config'; +import type { FireblocksIntegration } from '../src/fireblocks.ts'; + +const apiSecret = fs.readFileSync(`${__dirname}/fireblocks_secret_prod.key`, 'utf8'); + +const k = new Kiln({ + baseUrl: process.env.KILN_API_URL as string, + apiToken: process.env.KILN_API_KEY as string, +}); + +const vault: FireblocksIntegration = { + provider: 'fireblocks', + fireblocksApiKey: process.env.FIREBLOCKS_API_KEY as string, + fireblocksSecretKey: apiSecret, + vaultId: 17, +}; + +try { + console.log('crafting...'); + //@ts-ignore + const tx = await k.client.POST('/mantra/transaction/stake', { + body: { + account_id: process.env.KILN_ACCOUNT_ID as string, + pubkey: '028dfa6f41c655e38a0f8f2e3f3aa3e1246907a9bb299933f11996e2a345a21e10', + validator: 'mantravaloper146mj09yzu3mvz7pmy4dvs4z9wr2mst7ram37xw', + amount_uom: omToUom('0.01').toString(), + }, + }); + + console.log(tx); + console.log('signing...'); + //@ts-ignore + if (!tx.data?.data) throw new Error('No data in response'); + //@ts-ignore + const signResponse = await k.fireblocks.signMantraTx(vault, tx.data.data); + console.log('broadcasting...'); + if (!signResponse.signed_tx?.data?.signed_tx_serialized) throw new Error('No signed_tx in response'); + //@ts-ignore + const broadcastedTx = await k.client.POST('/mantra/transaction/broadcast', { + body: { + tx_serialized: signResponse.signed_tx.data.signed_tx_serialized, + }, + }); + console.log(broadcastedTx); +} catch (err) { + console.log(err); +} diff --git a/src/fireblocks.ts b/src/fireblocks.ts index f4e2223..c02f7df 100644 --- a/src/fireblocks.ts +++ b/src/fireblocks.ts @@ -345,6 +345,64 @@ export class FireblocksService { }; } + /** + * Sign a MANTRA transaction on Fireblocks + */ + async signMantraTx( + integration: FireblocksIntegration, + //@ts-ignore + tx: components['schemas']['MANTRAUnsignedTx'] | components['schemas']['MANTRAStakeUnsignedTx'], + note?: string, + ): Promise<{ + //@ts-ignore + signed_tx: { data: components['schemas']['MANTRASignedTx'] }; + fireblocks_tx: TransactionResponse; + }> { + const payload = { + rawMessageData: { + messages: [ + { + content: tx.unsigned_tx_hash, + derivationPath: [44, 118, integration.vaultId, 0, 0], + preHash: { + content: tx.unsigned_tx_serialized, + hashAlgorithm: 'SHA256', + }, + }, + ], + algorithm: SigningAlgorithm.MPC_ECDSA_SECP256K1, + }, + }; + + const fbSigner = this.getSigner(integration); + const fbNote = note ? note : 'MANTRA tx from @kilnfi/sdk'; + const fbTx = await fbSigner.sign(payload, undefined, fbNote); + const signature = fbTx.signedMessages?.[0]?.signature.fullSig; + + if (!signature) { + throw new Error('Fireblocks signature is missing'); + } + + //@ts-ignore + const preparedTx = await this.client.POST('/mantra/transaction/prepare', { + body: { + pubkey: tx.pubkey, + tx_body: tx.tx_body, + tx_auth_info: tx.tx_auth_info, + signature: signature, + }, + }); + + if (preparedTx.error) { + throw new Error('Failed to prepare transaction'); + } + + return { + signed_tx: preparedTx.data, + fireblocks_tx: fbTx, + }; + } + /** * Sign a INJ transaction on Fireblocks */ diff --git a/src/utils.ts b/src/utils.ts index d4383d5..5c4ed91 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -71,6 +71,13 @@ export const kavaToUkava = (kava: string): bigint => { return parseUnits(kava, 6); }; +/** + * Convert OM to uOM + */ +export const omToUom = (om: string): bigint => { + return parseUnits(om, 6); +}; + /** * Convert uZETA to ZETA */ @@ -176,6 +183,13 @@ export const ukavaToKava = (ukava: bigint): string => { return formatUnits(ukava, 6); }; +/** + * Convert uOM to OM + */ +export const uomToOm = (uom: bigint): string => { + return formatUnits(uom, 6); +}; + /** * Get a cosmos address from its public key and prefix * @param pubkey From 3f05019ced83790724ee9feff1e193288a10aad1 Mon Sep 17 00:00:00 2001 From: ilies Date: Mon, 23 Dec 2024 17:01:16 +0100 Subject: [PATCH 2/5] remove ts ignore example mantra --- examples/mantra.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/mantra.ts b/examples/mantra.ts index 5c876b0..1ac881a 100644 --- a/examples/mantra.ts +++ b/examples/mantra.ts @@ -19,25 +19,22 @@ const vault: FireblocksIntegration = { try { console.log('crafting...'); - //@ts-ignore const tx = await k.client.POST('/mantra/transaction/stake', { body: { account_id: process.env.KILN_ACCOUNT_ID as string, pubkey: '028dfa6f41c655e38a0f8f2e3f3aa3e1246907a9bb299933f11996e2a345a21e10', validator: 'mantravaloper146mj09yzu3mvz7pmy4dvs4z9wr2mst7ram37xw', amount_uom: omToUom('0.01').toString(), + restake_rewards: false, }, }); console.log(tx); console.log('signing...'); - //@ts-ignore if (!tx.data?.data) throw new Error('No data in response'); - //@ts-ignore const signResponse = await k.fireblocks.signMantraTx(vault, tx.data.data); console.log('broadcasting...'); if (!signResponse.signed_tx?.data?.signed_tx_serialized) throw new Error('No signed_tx in response'); - //@ts-ignore const broadcastedTx = await k.client.POST('/mantra/transaction/broadcast', { body: { tx_serialized: signResponse.signed_tx.data.signed_tx_serialized, From 87092d08914d3b43946f16f13a3fa35eb55e3b94 Mon Sep 17 00:00:00 2001 From: ilies Date: Mon, 23 Dec 2024 17:02:03 +0100 Subject: [PATCH 3/5] remove ts ignore example mantra --- src/fireblocks.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/fireblocks.ts b/src/fireblocks.ts index c02f7df..3230948 100644 --- a/src/fireblocks.ts +++ b/src/fireblocks.ts @@ -350,11 +350,9 @@ export class FireblocksService { */ async signMantraTx( integration: FireblocksIntegration, - //@ts-ignore tx: components['schemas']['MANTRAUnsignedTx'] | components['schemas']['MANTRAStakeUnsignedTx'], note?: string, ): Promise<{ - //@ts-ignore signed_tx: { data: components['schemas']['MANTRASignedTx'] }; fireblocks_tx: TransactionResponse; }> { @@ -383,7 +381,6 @@ export class FireblocksService { throw new Error('Fireblocks signature is missing'); } - //@ts-ignore const preparedTx = await this.client.POST('/mantra/transaction/prepare', { body: { pubkey: tx.pubkey, From 1d4a76182fd5441094f1a8fa24aaa40c85cb1418 Mon Sep 17 00:00:00 2001 From: ilies Date: Tue, 24 Dec 2024 11:57:29 +0100 Subject: [PATCH 4/5] remove restake rewards from example --- examples/mantra.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/mantra.ts b/examples/mantra.ts index 1ac881a..3c3d6c6 100644 --- a/examples/mantra.ts +++ b/examples/mantra.ts @@ -25,7 +25,6 @@ try { pubkey: '028dfa6f41c655e38a0f8f2e3f3aa3e1246907a9bb299933f11996e2a345a21e10', validator: 'mantravaloper146mj09yzu3mvz7pmy4dvs4z9wr2mst7ram37xw', amount_uom: omToUom('0.01').toString(), - restake_rewards: false, }, }); From 9a216e980de2a737d0e4e7788b9e030a16a7ec99 Mon Sep 17 00:00:00 2001 From: ilies Date: Tue, 24 Dec 2024 12:36:49 +0100 Subject: [PATCH 5/5] bump v4.0.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 10ef056..de2ba4a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kilnfi/sdk", - "version": "4.0.4", + "version": "4.0.5", "autor": "Kiln (https://kiln.fi)", "license": "BUSL-1.1", "description": "JavaScript sdk for Kiln API",