Skip to content

Commit e2e3a8e

Browse files
authored
eth-sign-and-broadcast (#61)
* eth-sign-and-broadcast * nit * move fireblocks destination id into integration * bump version to 2.1.0
1 parent 0df9a3d commit e2e3a8e

File tree

6 files changed

+134
-36
lines changed

6 files changed

+134
-36
lines changed

package-lock.json

Lines changed: 35 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@kilnfi/sdk",
3-
"version": "2.0.2",
3+
"version": "2.1.0",
44
"autor": "Kiln <[email protected]> (https://kiln.fi)",
55
"license": "BUSL-1.1",
66
"description": "JavaScript sdk for Kiln API",
@@ -43,7 +43,7 @@
4343
"crypto": "^1.0.1",
4444
"ethereumjs-util": "^7.1.5",
4545
"ethers": "^5.6.4",
46-
"fireblocks-sdk": "^2.3.2",
46+
"fireblocks-sdk": "^4.0.0",
4747
"js-sha256": "^0.9.0",
4848
"keccak256": "^1.0.6",
4949
"near-api-js": "^1.0.0",

src/integrations/fb_signer.ts

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@ import {
22
CreateTransactionResponse,
33
FireblocksSDK,
44
PeerType,
5+
TransactionArguments,
56
TransactionOperation,
67
TransactionResponse,
78
TransactionStatus,
89
} from 'fireblocks-sdk';
910

11+
import { utils } from 'ethers';
12+
import { EthTx } from '../types/eth';
13+
1014
type AssetId =
1115
'SOL_TEST'
1216
| 'SOL'
@@ -56,29 +60,63 @@ export class FbSigner {
5660

5761

5862
/**
59-
* Sign a transaction with fireblocks
63+
* Sign a transaction with fireblocks using Fireblocks raw message signing feature
6064
* @param payloadToSign: transaction data in hexadecimal
6165
* @param assetId: fireblocks asset id
62-
* @param note: fireblocks custom note
66+
* @param note: optional fireblocks custom note
6367
*/
6468
public async signWithFB(payloadToSign: any, assetId: AssetId, note?: string): Promise<TransactionResponse> {
6569
try {
66-
const fbTx = await this.fireblocks.createTransaction(
67-
{
68-
assetId: assetId,
69-
operation: TransactionOperation.RAW,
70-
source: {
71-
type: PeerType.VAULT_ACCOUNT,
72-
id: this.vaultId.toString(),
73-
},
74-
note,
75-
extraParameters: payloadToSign,
70+
const tx: TransactionArguments = {
71+
assetId: assetId,
72+
operation: TransactionOperation.RAW,
73+
source: {
74+
type: PeerType.VAULT_ACCOUNT,
75+
id: this.vaultId.toString(),
7676
},
77-
);
77+
note,
78+
extraParameters: payloadToSign,
79+
};
80+
const fbTx = await this.fireblocks.createTransaction(tx);
7881
return (await this.waitForTxCompletion(fbTx));
7982
} catch (err: any) {
8083
throw new Error('Fireblocks signer (signWithFB): ' + err);
8184
}
8285
}
86+
87+
/**
88+
* Sign and broadcast a transaction with fireblocks using Fireblocks contract call feature
89+
* @param payloadToSign: transaction data in hexadecimal
90+
* @param assetId: fireblocks asset id
91+
* @param note: optional fireblocks custom note
92+
* @param ethTx Ethereum transaction
93+
* @param destinationId Fireblocks destination id, this corresponds to the Fireblocks whitelisted contract address id
94+
*/
95+
public async signAndBroadcastWithFB(payloadToSign: any, assetId: AssetId, ethTx: EthTx, destinationId: string, note?: string): Promise<TransactionResponse> {
96+
try {
97+
const tx: TransactionArguments = {
98+
assetId: assetId,
99+
operation: TransactionOperation.CONTRACT_CALL,
100+
source: {
101+
type: PeerType.VAULT_ACCOUNT,
102+
id: this.vaultId.toString(),
103+
},
104+
destination: {
105+
type: PeerType.EXTERNAL_WALLET,
106+
id: destinationId,
107+
},
108+
amount: utils.formatEther(ethTx.data.amount_wei),
109+
note,
110+
extraParameters: payloadToSign,
111+
gasLimit: ethTx.data.gas_limit,
112+
priorityFee: utils.formatUnits(ethTx.data.max_priority_fee_per_gas_wei, 'gwei'),
113+
maxFee: utils.formatUnits(ethTx.data.max_fee_per_gas_wei, 'gwei'),
114+
};
115+
const fbTx = await this.fireblocks.createTransaction(tx);
116+
return (await this.waitForTxCompletion(fbTx));
117+
} catch (err: any) {
118+
throw new Error('Fireblocks signer (signAndBroadcastWithFB): ' + err);
119+
}
120+
}
83121
}
84122

src/services/eth.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { Service } from './service';
1313
import { utils } from 'ethers';
1414
import { ServiceProps } from '../types/service';
1515
import { Integration } from '../types/integrations';
16+
import { TransactionResponse } from 'fireblocks-sdk';
1617

1718
export class EthService extends Service {
1819
constructor({ testnet }: ServiceProps) {
@@ -47,7 +48,7 @@ export class EthService extends Service {
4748
/**
4849
* Sign transaction with given integration
4950
* @param integration custody solution to sign with
50-
* @param tx raw ada transaction
51+
* @param tx ETH transaction
5152
* @param note note to identify the transaction in your custody solution
5253
*/
5354
async sign(integration: Integration, tx: EthTx, note?: string): Promise<EthSignedTx> {
@@ -63,8 +64,13 @@ export class EthService extends Service {
6364
};
6465

6566
const fbSigner = this.getFbSigner(integration);
67+
const assetId = this.testnet ? 'ETH_TEST3' : 'ETH';
6668
const fbNote = note ? note : 'ETH tx from @kilnfi/sdk';
67-
const signatures = await fbSigner.signWithFB(payload, this.testnet ? 'ETH_TEST3' : 'ETH', fbNote);
69+
const signatures = await fbSigner.signWithFB(
70+
payload,
71+
assetId,
72+
fbNote,
73+
);
6874
const { data } = await api.post<EthSignedTx>(
6975
`/v1/eth/transaction/prepare`,
7076
{
@@ -79,6 +85,35 @@ export class EthService extends Service {
7985
}
8086
}
8187

88+
/**
89+
* Sign transaction with given integration using Fireblocks contract call feature
90+
* @param integration custody solution to sign with
91+
* @param tx ETH transaction
92+
* @param note note to identify the transaction in your custody solution
93+
*/
94+
async signAndBroadcast(integration: Integration, tx: EthTx, note?: string): Promise<TransactionResponse> {
95+
if(!integration.fireblocksDestinationId) {
96+
throw new Error('Fireblocks destination id is missing in integration');
97+
}
98+
try {
99+
const payload = {
100+
contractCallData: tx.data.contract_call_data,
101+
};
102+
103+
const fbSigner = this.getFbSigner(integration);
104+
const assetId = this.testnet ? 'ETH_TEST3' : 'ETH';
105+
const fbNote = note ? note : 'ETH tx from @kilnfi/sdk';
106+
return await fbSigner.signAndBroadcastWithFB(
107+
payload,
108+
assetId,
109+
tx,
110+
integration.fireblocksDestinationId,
111+
fbNote,
112+
);
113+
} catch (err: any) {
114+
throw new Error(err);
115+
}
116+
}
82117

83118
/**
84119
* Broadcast transaction to the network

src/types/eth.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ export type EthTx = {
6060
data: {
6161
unsigned_tx_hash: string;
6262
unsigned_tx_serialized: string;
63+
to: string;
64+
contract_call_data: string;
65+
amount_wei: string;
66+
nonce: number;
67+
gas_limit: number;
68+
max_priority_fee_per_gas_wei: string;
69+
max_fee_per_gas_wei: string;
70+
chain_id: number;
6371
}
6472
};
6573

src/types/integrations.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export type FireblocksIntegration = {
66
fireblocksSecretKey: string;
77
vaultId: number;
88
name?: string;
9+
fireblocksDestinationId?: string
910
};
1011

1112
export type Integration = FireblocksIntegration;

0 commit comments

Comments
 (0)