Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[fireblocks] Adjust confirmation threshold #171

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions chainio/clients/fireblocks/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ type Client interface {
// GetAssetAddresses makes a GetAssetAddresses request to the Fireblocks API
// It returns the addresses for the given asset ID and vault ID.
GetAssetAddresses(ctx context.Context, vaultID string, assetID AssetID) ([]AssetAddress, error)
// SetConfirmationThreshold makes a SetConfirmationThreshold request to the Fireblocks API
// It sets the confirmation threshold for the given transaction ID.
// By default, Fireblocks defaults the confirmation threshold to 3.
SetConfirmationThreshold(ctx context.Context, txID string, threshold int) (bool, error)
}

type client struct {
Expand Down
12 changes: 11 additions & 1 deletion chainio/clients/fireblocks/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func TestContractCall(t *testing.T) {
"2",
destinationAccountID,
"0",
"0x5140a548000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000002177dee1f66d6dbfbf517d9c4f316024c6a21aeb000000000000000000000000ad9770d6b5514724c7b766f087bea8a784038cbe000000000000000000000000cb14cfaac122e52024232583e7354589aede74ff00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000",
"0x5140a5480000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000300000000000000000000000008300f3797dcee9cc813ebaae25127b4e3b550e3000000000000000000000000ad9770d6b5514724c7b766f087bea8a784038cbe000000000000000000000000cb14cfaac122e52024232583e7354589aede74ff00000000000000000000000000000000000000000000000000000000000000010100000000000000000000000000000000000000000000000000000000000000",
"", // replaceTxByHash
"", // gasPrice
"", // gasLimit
Expand Down Expand Up @@ -99,3 +99,13 @@ func TestGetAssetAddresses(t *testing.T) {
t.Logf("Address: %+v", address)
}
}

func TestSetConfirmationThreshold(t *testing.T) {
t.Skip("skipping test as it's meant for manual runs only")

c := newFireblocksClient(t)
txID := "FILL_ME_IN"
success, err := c.SetConfirmationThreshold(context.Background(), txID, 1)
assert.NoError(t, err)
t.Logf("Success: %+v", success)
}
36 changes: 36 additions & 0 deletions chainio/clients/fireblocks/set_confirmation_threshold.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package fireblocks

import (
"context"
"encoding/json"
"fmt"
"strings"
)

type SetConfirmationThresholdRequest struct {
NumOfConfirmations int `json:"numOfConfirmations"`
}

type SetConfirmationThresholdResponse struct {
Success bool `json:"success"`
Transactions []string `json:"transactions"`
}

func (f *client) SetConfirmationThreshold(ctx context.Context, txID string, threshold int) (bool, error) {
f.logger.Debug("Fireblocks set confirmation threshold", "txID", txID, "threshold", threshold)
url := fmt.Sprintf("/v1/transactions/%s/set_confirmation_threshold", txID)
res, err := f.makeRequest(ctx, "POST", url, &SetConfirmationThresholdRequest{
NumOfConfirmations: threshold,
})
if err != nil {
return false, fmt.Errorf("error making request: %w", err)
}
var response SetConfirmationThresholdResponse
err = json.NewDecoder(strings.NewReader(string(res))).Decode(&response)
if err != nil {
return false, fmt.Errorf("error parsing response body: %w", err)
}
f.logger.Debug("Fireblocks set confirmation threshold response", "response", string(res))

return response.Success, nil
}
15 changes: 15 additions & 0 deletions chainio/clients/mocks/fireblocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 21 additions & 11 deletions chainio/clients/wallet/fireblocks_wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ type fireblocksWallet struct {
// accessed concurrently by SendTransaction and GetTransactionReceipt
mu sync.Mutex

fireblocksClient fireblocks.Client
ethClient eth.Client
vaultAccountName string
logger logging.Logger
chainID *big.Int
fireblocksClient fireblocks.Client
ethClient eth.Client
vaultAccountName string
logger logging.Logger
chainID *big.Int
confirmationThreshold int

// nonceToTx keeps track of the transaction ID for each nonce
// this is used to retrieve the transaction hash for a given nonce
Expand All @@ -47,18 +48,19 @@ type fireblocksWallet struct {
whitelistedContracts map[common.Address]*fireblocks.WhitelistedContract
}

func NewFireblocksWallet(fireblocksClient fireblocks.Client, ethClient eth.Client, vaultAccountName string, logger logging.Logger) (Wallet, error) {
func NewFireblocksWallet(fireblocksClient fireblocks.Client, ethClient eth.Client, vaultAccountName string, confirmationThreshold int, logger logging.Logger) (Wallet, error) {
chainID, err := ethClient.ChainID(context.Background())
if err != nil {
return nil, fmt.Errorf("error getting chain ID: %w", err)
}
logger.Debug("Creating new Fireblocks wallet for chain", "chainID", chainID)
return &fireblocksWallet{
fireblocksClient: fireblocksClient,
ethClient: ethClient,
vaultAccountName: vaultAccountName,
logger: logger,
chainID: chainID,
fireblocksClient: fireblocksClient,
ethClient: ethClient,
vaultAccountName: vaultAccountName,
logger: logger,
chainID: chainID,
confirmationThreshold: confirmationThreshold,

nonceToTxID: make(map[uint64]TxID),
txIDToNonce: make(map[TxID]uint64),
Expand Down Expand Up @@ -199,6 +201,14 @@ func (t *fireblocksWallet) SendTransaction(ctx context.Context, tx *types.Transa
t.txIDToNonce[res.ID] = nonce
t.logger.Debug("Fireblocks contract call complete", "txID", res.ID, "status", res.Status)

success, err := t.fireblocksClient.SetConfirmationThreshold(ctx, res.ID, t.confirmationThreshold)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use confirmationThreshold=0 or -1 to just use the default?

// if the confirmation threshold fails to be updated, the transaction will be confirmed by default after 3 confirmations
if err != nil {
t.logger.Error("failed to set confirmation threshold", "txID", res.ID, "threshold", t.confirmationThreshold, "error", err)
} else if !success {
t.logger.Error("failed to set confirmation threshold", "txID", res.ID, "threshold", t.confirmationThreshold)
}

return res.ID, nil
}

Expand Down
19 changes: 11 additions & 8 deletions chainio/clients/wallet/fireblocks_wallet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func TestSendTransaction(t *testing.T) {
logger, err := logging.NewZapLogger(logging.Development)
assert.NoError(t, err)
ethClient.EXPECT().ChainID(gomock.Any()).Return(big.NewInt(5), nil)
sender, err := wallet.NewFireblocksWallet(fireblocksClient, ethClient, vaultAccountName, logger)
sender, err := wallet.NewFireblocksWallet(fireblocksClient, ethClient, vaultAccountName, 1, logger)
assert.NoError(t, err)

fireblocksClient.EXPECT().ListContracts(gomock.Any()).Return([]fireblocks.WhitelistedContract{
Expand All @@ -55,6 +55,7 @@ func TestSendTransaction(t *testing.T) {
ID: "1234",
Status: fireblocks.Confirming,
}, nil)
fireblocksClient.EXPECT().SetConfirmationThreshold(gomock.Any(), gomock.Any(), 1).Return(true, nil)
fireblocksClient.EXPECT().ListVaultAccounts(gomock.Any()).Return([]fireblocks.VaultAccount{
{
ID: "vaultAccountID",
Expand Down Expand Up @@ -90,7 +91,7 @@ func TestSendTransactionNoValidContract(t *testing.T) {
logger, err := logging.NewZapLogger(logging.Development)
assert.NoError(t, err)
ethClient.EXPECT().ChainID(gomock.Any()).Return(big.NewInt(5), nil)
sender, err := wallet.NewFireblocksWallet(fireblocksClient, ethClient, vaultAccountName, logger)
sender, err := wallet.NewFireblocksWallet(fireblocksClient, ethClient, vaultAccountName, 1, logger)
assert.NoError(t, err)

fireblocksClient.EXPECT().ListContracts(gomock.Any()).Return([]fireblocks.WhitelistedContract{
Expand Down Expand Up @@ -146,7 +147,7 @@ func TestSendTransactionInvalidVault(t *testing.T) {
logger, err := logging.NewZapLogger(logging.Development)
assert.NoError(t, err)
ethClient.EXPECT().ChainID(gomock.Any()).Return(big.NewInt(5), nil)
sender, err := wallet.NewFireblocksWallet(fireblocksClient, ethClient, vaultAccountName, logger)
sender, err := wallet.NewFireblocksWallet(fireblocksClient, ethClient, vaultAccountName, 1, logger)
assert.NoError(t, err)

fireblocksClient.EXPECT().ListVaultAccounts(gomock.Any()).Return([]fireblocks.VaultAccount{
Expand Down Expand Up @@ -184,7 +185,7 @@ func TestSendTransactionReplaceTx(t *testing.T) {
logger, err := logging.NewZapLogger(logging.Development)
assert.NoError(t, err)
ethClient.EXPECT().ChainID(gomock.Any()).Return(big.NewInt(5), nil)
sender, err := wallet.NewFireblocksWallet(fireblocksClient, ethClient, vaultAccountName, logger)
sender, err := wallet.NewFireblocksWallet(fireblocksClient, ethClient, vaultAccountName, 1, logger)
assert.NoError(t, err)

fireblocksClient.EXPECT().ListContracts(gomock.Any()).Return([]fireblocks.WhitelistedContract{
Expand All @@ -209,6 +210,7 @@ func TestSendTransactionReplaceTx(t *testing.T) {
ID: "1234",
Status: fireblocks.Confirming,
}, nil)
fireblocksClient.EXPECT().SetConfirmationThreshold(gomock.Any(), gomock.Any(), 1).Return(true, nil)
fireblocksClient.EXPECT().ListVaultAccounts(gomock.Any()).Return([]fireblocks.VaultAccount{
{
ID: "vaultAccountID",
Expand Down Expand Up @@ -270,6 +272,7 @@ func TestSendTransactionReplaceTx(t *testing.T) {
ID: "5678",
Status: fireblocks.Confirming,
}, nil)
fireblocksClient.EXPECT().SetConfirmationThreshold(gomock.Any(), gomock.Any(), 1).Return(true, nil)
// send another tx with the same nonce
txID, err = sender.SendTransaction(context.Background(), replacementTx)
assert.NoError(t, err)
Expand All @@ -284,7 +287,7 @@ func TestWaitForTransactionReceipt(t *testing.T) {
logger, err := logging.NewZapLogger(logging.Development)
assert.NoError(t, err)
ethClient.EXPECT().ChainID(gomock.Any()).Return(big.NewInt(5), nil)
sender, err := wallet.NewFireblocksWallet(fireblocksClient, ethClient, vaultAccountName, logger)
sender, err := wallet.NewFireblocksWallet(fireblocksClient, ethClient, vaultAccountName, 1, logger)
assert.NoError(t, err)

expectedTxHash := "0x0000000000000000000000000000000000000000000000000000000000001234"
Expand Down Expand Up @@ -312,7 +315,7 @@ func TestWaitForTransactionReceiptFailFromFireblocks(t *testing.T) {
logger, err := logging.NewZapLogger(logging.Development)
assert.NoError(t, err)
ethClient.EXPECT().ChainID(gomock.Any()).Return(big.NewInt(5), nil)
sender, err := wallet.NewFireblocksWallet(fireblocksClient, ethClient, vaultAccountName, logger)
sender, err := wallet.NewFireblocksWallet(fireblocksClient, ethClient, vaultAccountName, 1, logger)
assert.NoError(t, err)

expectedTxHash := "0x0000000000000000000000000000000000000000000000000000000000001234"
Expand All @@ -335,7 +338,7 @@ func TestWaitForTransactionReceiptFailFromChain(t *testing.T) {
logger, err := logging.NewZapLogger(logging.Development)
assert.NoError(t, err)
ethClient.EXPECT().ChainID(gomock.Any()).Return(big.NewInt(5), nil)
sender, err := wallet.NewFireblocksWallet(fireblocksClient, ethClient, vaultAccountName, logger)
sender, err := wallet.NewFireblocksWallet(fireblocksClient, ethClient, vaultAccountName, 1, logger)
assert.NoError(t, err)

expectedTxHash := "0x0000000000000000000000000000000000000000000000000000000000001234"
Expand All @@ -359,7 +362,7 @@ func TestSenderAddress(t *testing.T) {
logger, err := logging.NewZapLogger(logging.Development)
assert.NoError(t, err)
ethClient.EXPECT().ChainID(gomock.Any()).Return(big.NewInt(5), nil)
w, err := wallet.NewFireblocksWallet(fireblocksClient, ethClient, vaultAccountName, logger)
w, err := wallet.NewFireblocksWallet(fireblocksClient, ethClient, vaultAccountName, 1, logger)
assert.NoError(t, err)
assetID := fireblocks.AssetIDByChain[5]
fireblocksClient.EXPECT().ListVaultAccounts(gomock.Any()).Return([]fireblocks.VaultAccount{
Expand Down
Loading