Skip to content

Commit

Permalink
add signNobleTx and noble example
Browse files Browse the repository at this point in the history
  • Loading branch information
nooxx committed Nov 14, 2024
1 parent 5479cbc commit 7c67252
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 1 deletion.
Binary file modified bun.lockb
Binary file not shown.
56 changes: 56 additions & 0 deletions examples/noble.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Kiln, usdcToUusdc } from "../src/kiln";
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: 37
};

try {
console.log('crafting...');
// const s = await k.fireblocks.getSdk(vault);
// const p = await s.getPublicKeyInfoForVaultAccount({
// assetId: "DYDX_DYDX",
// compressed: true,
// vaultAccountId: 37,
// change: 0,
// addressIndex: 0,
// });
// console.log(getCosmosAddress('02d92b48d3e9ef34f2016eac7857a02768c88e30aea7a2366bc5ba032a22eceb8b', 'noble'));
const tx = await k.client.POST(
'/v1/noble/transaction/burn-usdc',
{
body: {
pubkey: '02d92b48d3e9ef34f2016eac7857a02768c88e30aea7a2366bc5ba032a22eceb8b',
recipient: '0xBC86717BaD3F8CcF86d2882a6bC351C94580A994',
amount_uusdc: usdcToUusdc('0.01').toString(),
}
}
);
console.log('signing...');
if(!tx.data?.data) throw new Error('No data in response');
const signResponse = await k.fireblocks.signNobleTx(vault, tx.data.data);
console.log('broadcasting...');
if(!signResponse.signed_tx?.data?.signed_tx_serialized) throw new Error('No signed_tx in response');
const broadcastedTx = await k.client.POST("/v1/noble/transaction/broadcast", {
body: {
tx_serialized: signResponse.signed_tx.data.signed_tx_serialized,
}
});
console.log(broadcastedTx);

} catch (err) {
console.log(err);
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"homepage": "https://github.com/kilnfi/sdk-js#readme",
"dependencies": {
"@types/bun": "^1.1.11",
"bech32": "^2.0.0",
"fireblocks-sdk": "^5.32.0",
"openapi-fetch": "^0.12.0",
"viem": "^2.21.29"
Expand Down
55 changes: 55 additions & 0 deletions src/fireblocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,61 @@ export class FireblocksService {
};
}

/**
* Sign a NOBLE transaction on Fireblocks
*/
async signNobleTx(
integration: FireblocksIntegration,
tx: components['schemas']['DYDXUnsignedTx'],
note?: string,
): Promise<{
signed_tx: { data: components['schemas']['DYDXSignedTx'] };
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 : 'NOBLE 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');
}

const preparedTx = await this.client.POST('/v1/noble/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 OSMO transaction on Fireblocks
*/
Expand Down
40 changes: 39 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { formatUnits, parseUnits } from 'viem';
import { formatUnits, parseUnits, ripemd160, sha256 } from "viem";
import { bech32 } from "bech32";

/**
* Convert wei to ETH
Expand Down Expand Up @@ -161,9 +162,46 @@ export const uusdcToUsdc = (uusdc: bigint): string => {
return formatUnits(uusdc, 6);
};

/**
* Convert USDC to uUSDC
*/
export const usdcToUusdc = (usdc: string): bigint => {
return parseUnits(usdc, 6);
};

/**
* Convert uKAVA to KAVA
*/
export const ukavaToKava = (ukava: bigint): string => {
return formatUnits(ukava, 6);
};


/**
* Get a cosmos address from its public key and prefix
* @param pubkey
* @param prefix
*/
export const getCosmosAddress = (pubkey: string, prefix: string): string => {
const compressed_pubkey = compressPublicKey(pubkey);
const hash = sha256(Uint8Array.from(Buffer.from(compressed_pubkey, "hex")));
const raw_addr = ripemd160(hash, "bytes")
return bech32.encode(prefix, bech32.toWords(raw_addr));
};

/**
* Compress a cosmos public key
* @param pubkey
*/
export const compressPublicKey = (pubkey: string): string => {
const pub_key_buffer = new Uint8Array(Buffer.from(pubkey, "hex"));
if (pub_key_buffer.length !== 65) return pubkey;
const x = pub_key_buffer.slice(1, 33);
const y = pub_key_buffer.slice(33);
// We will add 0x02 if the last bit isn't set, otherwise we will add 0x03
// @ts-ignore
const prefix = y[y.length - 1] & 1 ? "03" : "02";
// Concatenate the prefix and the x value to get the compressed key
const compressed_key = Buffer.concat([new Uint8Array(Buffer.from(prefix, "hex")), x]);
return compressed_key.toString("hex");
};

0 comments on commit 7c67252

Please sign in to comment.