From 6167806e34f8353c0ad848429d65574fa5bcf9b0 Mon Sep 17 00:00:00 2001 From: woodenfurniture <125113430+woodenfurniture@users.noreply.github.com> Date: Tue, 10 Oct 2023 14:29:47 +1100 Subject: [PATCH 01/12] fix: validate addresses during tx broadcast --- packages/chain-adapters/src/api.ts | 6 ++- .../src/cosmossdk/CosmosSdkBaseAdapter.ts | 13 +++++- .../cosmossdk/cosmos/CosmosChainAdapter.ts | 10 ++++- .../thorchain/ThorchainChainAdapter.ts | 11 ++++- .../chain-adapters/src/evm/EvmBaseAdapter.ts | 14 ++++++- .../evm/arbitrum/ArbitrumChainAdapter.test.ts | 26 ++++++++---- .../avalanche/AvalancheChainAdapter.test.ts | 26 ++++++++---- .../evm/bnbsmartchain/BscChainAdapter.test.ts | 26 ++++++++---- .../evm/ethereum/EthereumChainAdapter.test.ts | 26 ++++++++---- .../src/evm/gnosis/GnosisChainAdapter.test.ts | 27 ++++++++---- .../evm/optimism/OptimismChainAdapter.test.ts | 22 ++++++---- .../evm/polygon/PolygonChainAdapter.test.ts | 26 ++++++++---- packages/chain-adapters/src/types.ts | 12 ++++++ .../src/utils/validateAddress.ts | 41 +++++++++++++++++++ .../src/utxo/UtxoBaseAdapter.ts | 18 ++++++-- .../utxo/bitcoin/BitcoinChainAdapter.test.ts | 10 ++++- .../BitcoinCashChainAdapter.test.ts | 10 ++++- .../dogecoin/DogecoinChainAdapter.test.ts | 10 ++++- .../litecoin/LitecoinChainAdapter.test.ts | 10 ++++- src/components/Modals/Send/utils.ts | 20 ++++----- .../useTradeExecution/useTradeExecution.ts | 14 +++++-- src/context/PluginProvider/PluginProvider.tsx | 2 +- src/lib/investor/investor-foxy/api/api.ts | 14 ++++++- .../investor/investor-idle/IdleOpportunity.ts | 13 +++++- .../investor-yearn/YearnOpportunity.ts | 16 ++++++-- src/lib/swapper/types.ts | 1 + src/lib/utils/evm.ts | 32 +++++++++++---- .../useStakingAction/useStakingAction.tsx | 10 +++-- src/plugins/index.ts | 4 ++ .../utils/EIP155RequestHandlerUtil.ts | 7 +++- 30 files changed, 364 insertions(+), 113 deletions(-) create mode 100644 packages/chain-adapters/src/utils/validateAddress.ts diff --git a/packages/chain-adapters/src/api.ts b/packages/chain-adapters/src/api.ts index afb84abc23e..3053fd700f9 100644 --- a/packages/chain-adapters/src/api.ts +++ b/packages/chain-adapters/src/api.ts @@ -3,11 +3,13 @@ import type { BIP44Params, UtxoAccountType } from '@shapeshiftoss/types' import type { Account, + BroadcastTransactionInput, BuildSendTxInput, FeeDataEstimate, GetAddressInput, GetBIP44ParamsInput, GetFeeDataInput, + SignAndBroadcastTransactionInput, SignTx, SignTxInput, SubscribeError, @@ -65,11 +67,11 @@ export type ChainAdapter = { signTransaction(signTxInput: SignTxInput>): Promise - signAndBroadcastTransaction?(signTxInput: SignTxInput>): Promise + signAndBroadcastTransaction?(input: SignAndBroadcastTransactionInput): Promise getFeeData(input: Partial>): Promise> - broadcastTransaction(hex: string): Promise + broadcastTransaction(input: BroadcastTransactionInput): Promise validateAddress(address: string): Promise diff --git a/packages/chain-adapters/src/cosmossdk/CosmosSdkBaseAdapter.ts b/packages/chain-adapters/src/cosmossdk/CosmosSdkBaseAdapter.ts index 3046d43cc70..ba5aa7cd98d 100644 --- a/packages/chain-adapters/src/cosmossdk/CosmosSdkBaseAdapter.ts +++ b/packages/chain-adapters/src/cosmossdk/CosmosSdkBaseAdapter.ts @@ -17,12 +17,14 @@ import type { ChainAdapter as IChainAdapter } from '../api' import { ErrorHandler } from '../error/ErrorHandler' import type { Account, + BroadcastTransactionInput, BuildSendApiTxInput, BuildSendTxInput, FeeDataEstimate, GetAddressInput, GetBIP44ParamsInput, GetFeeDataInput, + SignAndBroadcastTransactionInput, SignTx, SignTxInput, SubscribeError, @@ -35,6 +37,7 @@ import type { import { ValidAddressResultType } from '../types' import { toAddressNList, toRootDerivationPath } from '../utils' import { bnOrZero } from '../utils/bignumber' +import { validateAddress } from '../utils/validateAddress' import type { cosmos, thorchain } from './' import type { BuildTransactionInput, @@ -152,7 +155,11 @@ export abstract class CosmosSdkBaseAdapter implement abstract getAddress(input: GetAddressInput): Promise abstract getFeeData(input: Partial>): Promise> abstract signTransaction(signTxInput: SignTxInput>): Promise - abstract signAndBroadcastTransaction(signTxInput: SignTxInput>): Promise + abstract signAndBroadcastTransaction({ + from, + to, + signTxInput, + }: SignAndBroadcastTransactionInput): Promise getChainId(): ChainId { return this.chainId @@ -329,7 +336,9 @@ export abstract class CosmosSdkBaseAdapter implement return { txToSign } } - broadcastTransaction(hex: string): Promise { + async broadcastTransaction({ from, to, hex }: BroadcastTransactionInput): Promise { + await Promise.all([validateAddress(from), validateAddress(to)]) + try { return this.providers.http.sendTx({ body: { rawTx: hex } }) } catch (err) { diff --git a/packages/chain-adapters/src/cosmossdk/cosmos/CosmosChainAdapter.ts b/packages/chain-adapters/src/cosmossdk/cosmos/CosmosChainAdapter.ts index 716f8d42e09..c7e85b42efe 100644 --- a/packages/chain-adapters/src/cosmossdk/cosmos/CosmosChainAdapter.ts +++ b/packages/chain-adapters/src/cosmossdk/cosmos/CosmosChainAdapter.ts @@ -17,10 +17,12 @@ import type { FeeDataEstimate, GetAddressInput, GetFeeDataInput, + SignAndBroadcastTransactionInput, SignTxInput, } from '../../types' import { ChainAdapterDisplayName } from '../../types' import { bn, calcFee, toAddressNList } from '../../utils' +import { validateAddress } from '../../utils/validateAddress' import type { ChainAdapterArgs } from '../CosmosSdkBaseAdapter' import { assertIsValidatorAddress, CosmosSdkBaseAdapter } from '../CosmosSdkBaseAdapter' import type { Message, ValidatorAction } from '../types' @@ -280,7 +282,13 @@ export class ChainAdapter extends CosmosSdkBaseAdapter): Promise { + async signAndBroadcastTransaction({ + from, + to, + signTxInput, + }: SignAndBroadcastTransactionInput): Promise { + await Promise.all([validateAddress(from), validateAddress(to)]) + const { wallet } = signTxInput try { if (supportsCosmos(wallet)) { diff --git a/packages/chain-adapters/src/cosmossdk/thorchain/ThorchainChainAdapter.ts b/packages/chain-adapters/src/cosmossdk/thorchain/ThorchainChainAdapter.ts index 49bbdca636b..cfec6ffed4b 100644 --- a/packages/chain-adapters/src/cosmossdk/thorchain/ThorchainChainAdapter.ts +++ b/packages/chain-adapters/src/cosmossdk/thorchain/ThorchainChainAdapter.ts @@ -14,11 +14,13 @@ import type { FeeDataEstimate, GetAddressInput, GetFeeDataInput, + SignAndBroadcastTransactionInput, SignTxInput, } from '../../types' import { ChainAdapterDisplayName } from '../../types' import { toAddressNList } from '../../utils' import { bnOrZero } from '../../utils/bignumber' +import { validateAddress } from '../../utils/validateAddress' import type { ChainAdapterArgs } from '../CosmosSdkBaseAdapter' import { CosmosSdkBaseAdapter } from '../CosmosSdkBaseAdapter' import type { Message } from '../types' @@ -204,8 +206,15 @@ export class ChainAdapter extends CosmosSdkBaseAdapter): Promise { + async signAndBroadcastTransaction({ + from, + to, + signTxInput, + }: SignAndBroadcastTransactionInput): Promise { + await Promise.all([validateAddress(from), validateAddress(to)]) + const { wallet } = signTxInput + try { if (supportsThorchain(wallet)) { const signedTx = await this.signTransaction(signTxInput) diff --git a/packages/chain-adapters/src/evm/EvmBaseAdapter.ts b/packages/chain-adapters/src/evm/EvmBaseAdapter.ts index 2a77c4af321..3575411211d 100644 --- a/packages/chain-adapters/src/evm/EvmBaseAdapter.ts +++ b/packages/chain-adapters/src/evm/EvmBaseAdapter.ts @@ -27,12 +27,14 @@ import type { ChainAdapter as IChainAdapter } from '../api' import { ErrorHandler } from '../error/ErrorHandler' import type { Account, + BroadcastTransactionInput, BuildSendApiTxInput, BuildSendTxInput, FeeDataEstimate, GetAddressInput, GetBIP44ParamsInput, GetFeeDataInput, + SignAndBroadcastTransactionInput, SignMessageInput, SignTx, SignTxInput, @@ -47,6 +49,7 @@ import type { import { ValidAddressResultType } from '../types' import { getAssetNamespace, toAddressNList, toRootDerivationPath } from '../utils' import { bnOrZero } from '../utils/bignumber' +import { validateAddress } from '../utils/validateAddress' import type { arbitrum, avalanche, bnbsmartchain, ethereum, gnosis, optimism, polygon } from '.' import type { BuildCustomApiTxInput, @@ -442,7 +445,13 @@ export abstract class EvmBaseAdapter implements IChainAdap } } - async signAndBroadcastTransaction(signTxInput: SignTxInput): Promise { + async signAndBroadcastTransaction({ + from, + to, + signTxInput, + }: SignAndBroadcastTransactionInput): Promise { + await Promise.all([validateAddress(from), validateAddress(to)]) + try { const { txToSign, wallet } = signTxInput @@ -461,7 +470,8 @@ export abstract class EvmBaseAdapter implements IChainAdap } } - broadcastTransaction(hex: string): Promise { + async broadcastTransaction({ from, to, hex }: BroadcastTransactionInput): Promise { + await Promise.all([validateAddress(from), validateAddress(to)]) return this.providers.http.sendTx({ sendTxBody: { hex } }) } diff --git a/packages/chain-adapters/src/evm/arbitrum/ArbitrumChainAdapter.test.ts b/packages/chain-adapters/src/evm/arbitrum/ArbitrumChainAdapter.test.ts index 527dcda67cd..36869065d90 100644 --- a/packages/chain-adapters/src/evm/arbitrum/ArbitrumChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/arbitrum/ArbitrumChainAdapter.test.ts @@ -21,6 +21,10 @@ import { bn } from '../../utils/bignumber' import type { ChainAdapterArgs, EvmChainId } from '../EvmBaseAdapter' import * as arbitrum from './ArbitrumChainAdapter' +jest.mock('../utils/validatePubkey', () => ({ + validatePubkey: jest.fn(), +})) + const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' const EOA_ADDRESS = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' @@ -318,11 +322,11 @@ describe('ArbitrumChainAdapter', () => { wallet.ethSendTx = async () => await Promise.resolve(null) - const tx = { wallet, txToSign: {} } as unknown as SignTxInput + const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput - await expect(adapter.signAndBroadcastTransaction(tx)).rejects.toThrow( - /Error signing & broadcasting tx/, - ) + await expect( + adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + ).rejects.toThrow(/Error signing & broadcasting tx/) }) it('should return the hash returned by wallet.ethSendTx', async () => { @@ -334,11 +338,11 @@ describe('ArbitrumChainAdapter', () => { hash: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', }) - const tx = { wallet, txToSign: {} } as unknown as SignTxInput + const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput - await expect(adapter.signAndBroadcastTransaction(tx)).resolves.toEqual( - '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', - ) + await expect( + adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + ).resolves.toEqual('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331') }) }) @@ -392,7 +396,11 @@ describe('ArbitrumChainAdapter', () => { const adapter = new arbitrum.ChainAdapter(args) const mockTx = '0x123' - const result = await adapter.broadcastTransaction(mockTx) + const result = await adapter.broadcastTransaction({ + from: '0x1234', + to: '0x1234', + hex: mockTx, + }) expect(args.providers.http.sendTx).toHaveBeenCalledWith({ sendTxBody: { hex: mockTx } }) expect(result).toEqual(expectedResult) diff --git a/packages/chain-adapters/src/evm/avalanche/AvalancheChainAdapter.test.ts b/packages/chain-adapters/src/evm/avalanche/AvalancheChainAdapter.test.ts index 3ee819f830a..236f9d34253 100644 --- a/packages/chain-adapters/src/evm/avalanche/AvalancheChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/avalanche/AvalancheChainAdapter.test.ts @@ -26,6 +26,10 @@ import { bn } from '../../utils/bignumber' import type { ChainAdapterArgs, EvmChainId } from '../EvmBaseAdapter' import * as avalanche from './AvalancheChainAdapter' +jest.mock('../utils/validatePubkey', () => ({ + validatePubkey: jest.fn(), +})) + const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' const EOA_ADDRESS = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' @@ -323,11 +327,11 @@ describe('AvalancheChainAdapter', () => { wallet.ethSendTx = async () => await Promise.resolve(null) - const tx = { wallet, txToSign: {} } as unknown as SignTxInput + const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput - await expect(adapter.signAndBroadcastTransaction(tx)).rejects.toThrow( - /Error signing & broadcasting tx/, - ) + await expect( + adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + ).rejects.toThrow(/Error signing & broadcasting tx/) }) it('should return the hash returned by wallet.ethSendTx', async () => { @@ -339,11 +343,11 @@ describe('AvalancheChainAdapter', () => { hash: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', }) - const tx = { wallet, txToSign: {} } as unknown as SignTxInput + const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput - await expect(adapter.signAndBroadcastTransaction(tx)).resolves.toEqual( - '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', - ) + await expect( + adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + ).resolves.toEqual('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331') }) }) @@ -397,7 +401,11 @@ describe('AvalancheChainAdapter', () => { const adapter = new avalanche.ChainAdapter(args) const mockTx = '0x123' - const result = await adapter.broadcastTransaction(mockTx) + const result = await adapter.broadcastTransaction({ + from: '0x1234', + to: '0x1234', + hex: mockTx, + }) expect(args.providers.http.sendTx).toHaveBeenCalledWith({ sendTxBody: { hex: mockTx } }) expect(result).toEqual(expectedResult) diff --git a/packages/chain-adapters/src/evm/bnbsmartchain/BscChainAdapter.test.ts b/packages/chain-adapters/src/evm/bnbsmartchain/BscChainAdapter.test.ts index b28c12c3eeb..1be6eb486a7 100644 --- a/packages/chain-adapters/src/evm/bnbsmartchain/BscChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/bnbsmartchain/BscChainAdapter.test.ts @@ -21,6 +21,10 @@ import { bn } from '../../utils/bignumber' import type { ChainAdapterArgs, EvmChainId } from '../EvmBaseAdapter' import * as bsc from './BscChainAdapter' +jest.mock('../utils/validatePubkey', () => ({ + validatePubkey: jest.fn(), +})) + const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' const EOA_ADDRESS = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' @@ -300,11 +304,11 @@ describe('BscChainAdapter', () => { wallet.ethSendTx = async () => await Promise.resolve(null) - const tx = { wallet, txToSign: {} } as unknown as SignTxInput + const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput - await expect(adapter.signAndBroadcastTransaction(tx)).rejects.toThrow( - /Error signing & broadcasting tx/, - ) + await expect( + adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + ).rejects.toThrow(/Error signing & broadcasting tx/) }) it('should return the hash returned by wallet.ethSendTx', async () => { @@ -316,11 +320,11 @@ describe('BscChainAdapter', () => { hash: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', }) - const tx = { wallet, txToSign: {} } as unknown as SignTxInput + const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput - await expect(adapter.signAndBroadcastTransaction(tx)).resolves.toEqual( - '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', - ) + await expect( + adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + ).resolves.toEqual('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331') }) }) @@ -374,7 +378,11 @@ describe('BscChainAdapter', () => { const adapter = new bsc.ChainAdapter(args) const mockTx = '0x123' - const result = await adapter.broadcastTransaction(mockTx) + const result = await adapter.broadcastTransaction({ + from: '0x1234', + to: '0x1234', + hex: mockTx, + }) expect(args.providers.http.sendTx).toHaveBeenCalledWith({ sendTxBody: { hex: mockTx } }) expect(result).toEqual(expectedResult) diff --git a/packages/chain-adapters/src/evm/ethereum/EthereumChainAdapter.test.ts b/packages/chain-adapters/src/evm/ethereum/EthereumChainAdapter.test.ts index 54155a11cd6..4dae9c387ec 100644 --- a/packages/chain-adapters/src/evm/ethereum/EthereumChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/ethereum/EthereumChainAdapter.test.ts @@ -21,6 +21,10 @@ import { bn } from '../../utils/bignumber' import type { ChainAdapterArgs, EvmChainId } from '../EvmBaseAdapter' import * as ethereum from './EthereumChainAdapter' +jest.mock('../utils/validatePubkey', () => ({ + validatePubkey: jest.fn(), +})) + const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' const EOA_ADDRESS = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' const ENS_NAME = 'vitalik.eth' @@ -357,11 +361,11 @@ describe('EthereumChainAdapter', () => { wallet.ethSendTx = async () => await Promise.resolve(null) - const tx = { wallet, txToSign: {} } as unknown as SignTxInput + const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput - await expect(adapter.signAndBroadcastTransaction(tx)).rejects.toThrow( - /Error signing & broadcasting tx/, - ) + await expect( + adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + ).rejects.toThrow(/Error signing & broadcasting tx/) }) it('should return the hash returned by wallet.ethSendTx', async () => { @@ -373,11 +377,11 @@ describe('EthereumChainAdapter', () => { hash: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', }) - const tx = { wallet, txToSign: {} } as unknown as SignTxInput + const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput - await expect(adapter.signAndBroadcastTransaction(tx)).resolves.toEqual( - '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', - ) + await expect( + adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + ).resolves.toEqual('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331') }) }) @@ -431,7 +435,11 @@ describe('EthereumChainAdapter', () => { const adapter = new ethereum.ChainAdapter(args) const mockTx = '0x123' - const result = await adapter.broadcastTransaction(mockTx) + const result = await adapter.broadcastTransaction({ + from: '0x1234', + to: '0x1234', + hex: mockTx, + }) expect(args.providers.http.sendTx).toHaveBeenCalledWith({ sendTxBody: { hex: mockTx } }) expect(result).toEqual(expectedResult) diff --git a/packages/chain-adapters/src/evm/gnosis/GnosisChainAdapter.test.ts b/packages/chain-adapters/src/evm/gnosis/GnosisChainAdapter.test.ts index f6a3f1d1180..cba9cb9ee30 100644 --- a/packages/chain-adapters/src/evm/gnosis/GnosisChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/gnosis/GnosisChainAdapter.test.ts @@ -21,6 +21,11 @@ import { toAddressNList } from '../../utils' import { bn } from '../../utils/bignumber' import type { ChainAdapterArgs, EvmChainId } from '../EvmBaseAdapter' import * as gnosis from './GnosisChainAdapter' + +jest.mock('../utils/validatePubkey', () => ({ + validatePubkey: jest.fn(), +})) + const EOA_ADDRESS = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' const testMnemonic = 'alcohol woman abuse must during monitor noble actual mixed trade anger aisle' @@ -317,11 +322,11 @@ describe('GnosisChainAdapter', () => { wallet.ethSendTx = async () => await Promise.resolve(null) - const tx = { wallet, txToSign: {} } as unknown as SignTxInput + const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput - await expect(adapter.signAndBroadcastTransaction(tx)).rejects.toThrow( - /Error signing & broadcasting tx/, - ) + await expect( + adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + ).rejects.toThrow(/Error signing & broadcasting tx/) }) it('should return the hash returned by wallet.ethSendTx', async () => { @@ -333,11 +338,11 @@ describe('GnosisChainAdapter', () => { hash: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', }) - const tx = { wallet, txToSign: {} } as unknown as SignTxInput + const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput - await expect(adapter.signAndBroadcastTransaction(tx)).resolves.toEqual( - '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', - ) + await expect( + adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + ).resolves.toEqual('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331') }) }) @@ -391,7 +396,11 @@ describe('GnosisChainAdapter', () => { const adapter = new gnosis.ChainAdapter(args) const mockTx = '0x123' - const result = await adapter.broadcastTransaction(mockTx) + const result = await adapter.broadcastTransaction({ + from: '0x1234', + to: '0x1234', + hex: mockTx, + }) expect(args.providers.http.sendTx).toHaveBeenCalledWith({ sendTxBody: { hex: mockTx } }) expect(result).toEqual(expectedResult) diff --git a/packages/chain-adapters/src/evm/optimism/OptimismChainAdapter.test.ts b/packages/chain-adapters/src/evm/optimism/OptimismChainAdapter.test.ts index c13b8ae3eb2..f27c0f42ca1 100644 --- a/packages/chain-adapters/src/evm/optimism/OptimismChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/optimism/OptimismChainAdapter.test.ts @@ -329,11 +329,11 @@ describe('OptimismChainAdapter', () => { wallet.ethSendTx = async () => await Promise.resolve(null) - const tx = { wallet, txToSign: {} } as unknown as SignTxInput + const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput - await expect(adapter.signAndBroadcastTransaction(tx)).rejects.toThrow( - /Error signing & broadcasting tx/, - ) + await expect( + adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + ).rejects.toThrow(/Error signing & broadcasting tx/) }) it('should return the hash returned by wallet.ethSendTx', async () => { @@ -345,11 +345,11 @@ describe('OptimismChainAdapter', () => { hash: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', }) - const tx = { wallet, txToSign: {} } as unknown as SignTxInput + const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput - await expect(adapter.signAndBroadcastTransaction(tx)).resolves.toEqual( - '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', - ) + await expect( + adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + ).resolves.toEqual('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331') }) }) @@ -403,7 +403,11 @@ describe('OptimismChainAdapter', () => { const adapter = new optimism.ChainAdapter(args) const mockTx = '0x123' - const result = await adapter.broadcastTransaction(mockTx) + const result = await adapter.broadcastTransaction({ + from: '0x1234', + to: '0x1234', + hex: mockTx, + }) expect(args.providers.http.sendTx).toHaveBeenCalledWith({ sendTxBody: { hex: mockTx } }) expect(result).toEqual(expectedResult) diff --git a/packages/chain-adapters/src/evm/polygon/PolygonChainAdapter.test.ts b/packages/chain-adapters/src/evm/polygon/PolygonChainAdapter.test.ts index b6304b5f8de..d9d9969c4f5 100644 --- a/packages/chain-adapters/src/evm/polygon/PolygonChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/polygon/PolygonChainAdapter.test.ts @@ -21,6 +21,10 @@ import { bn } from '../../utils/bignumber' import type { ChainAdapterArgs, EvmChainId } from '../EvmBaseAdapter' import * as polygon from './PolygonChainAdapter' +jest.mock('../utils/validatePubkey', () => ({ + validatePubkey: jest.fn(), +})) + const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' const EOA_ADDRESS = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' @@ -318,11 +322,11 @@ describe('PolygonChainAdapter', () => { wallet.ethSendTx = async () => await Promise.resolve(null) - const tx = { wallet, txToSign: {} } as unknown as SignTxInput + const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput - await expect(adapter.signAndBroadcastTransaction(tx)).rejects.toThrow( - /Error signing & broadcasting tx/, - ) + await expect( + adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + ).rejects.toThrow(/Error signing & broadcasting tx/) }) it('should return the hash returned by wallet.ethSendTx', async () => { @@ -334,11 +338,11 @@ describe('PolygonChainAdapter', () => { hash: '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', }) - const tx = { wallet, txToSign: {} } as unknown as SignTxInput + const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput - await expect(adapter.signAndBroadcastTransaction(tx)).resolves.toEqual( - '0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331', - ) + await expect( + adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + ).resolves.toEqual('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331') }) }) @@ -392,7 +396,11 @@ describe('PolygonChainAdapter', () => { const adapter = new polygon.ChainAdapter(args) const mockTx = '0x123' - const result = await adapter.broadcastTransaction(mockTx) + const result = await adapter.broadcastTransaction({ + from: '0x1234', + to: '0x1234', + hex: mockTx, + }) expect(args.providers.http.sendTx).toHaveBeenCalledWith({ sendTxBody: { hex: mockTx } }) expect(result).toEqual(expectedResult) diff --git a/packages/chain-adapters/src/types.ts b/packages/chain-adapters/src/types.ts index b5364c98a87..adec75bb28f 100644 --- a/packages/chain-adapters/src/types.ts +++ b/packages/chain-adapters/src/types.ts @@ -316,3 +316,15 @@ export enum ChainAdapterDisplayName { Dogecoin = 'Dogecoin', Litecoin = 'Litecoin', } + +export type BroadcastTransactionInput = { + from: string + to: string + hex: string +} + +export type SignAndBroadcastTransactionInput = { + from: string + to: string + signTxInput: SignTxInput> +} diff --git a/packages/chain-adapters/src/utils/validateAddress.ts b/packages/chain-adapters/src/utils/validateAddress.ts new file mode 100644 index 00000000000..739bfb1565e --- /dev/null +++ b/packages/chain-adapters/src/utils/validateAddress.ts @@ -0,0 +1,41 @@ +import axios from 'axios' + +const TRM_LABS_API_URL = 'https://api.trmlabs.com/public/v1/sanctions/screening' + +const cache: Record> = {} + +const _validateAddress = async (address: string): Promise => { + type trmResponse = [ + { + address: string + isSanctioned: boolean + }, + ] + + const response = await axios.post( + TRM_LABS_API_URL, + [ + { + address, + }, + ], + { + headers: { + Accept: 'application/json', + }, + }, + ) + + return response.data[0].isSanctioned +} + +export const validateAddress = async (address: string): Promise => { + // dedupe and cache promises in memory + if (cache[address] === undefined) { + const newEntry = _validateAddress(address) + cache[address] = newEntry + } + + const result = cache[address] + if (await result) throw Error('Address not supported') +} diff --git a/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts b/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts index b25424f0c8f..298b9aaf785 100644 --- a/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts +++ b/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts @@ -19,10 +19,12 @@ import type { ChainAdapter as IChainAdapter } from '../api' import { ErrorHandler } from '../error/ErrorHandler' import type { Account, + BroadcastTransactionInput, BuildSendTxInput, FeeDataEstimate, GetBIP44ParamsInput, GetFeeDataInput, + SignAndBroadcastTransactionInput, SignTx, SignTxInput, SubscribeError, @@ -43,6 +45,7 @@ import { toRootDerivationPath, } from '../utils' import { bnOrZero } from '../utils/bignumber' +import { validateAddress } from '../utils/validateAddress' import type { bitcoin, bitcoincash, dogecoin, litecoin } from './' import type { GetAddressInput } from './types' import { utxoSelect } from './utxoSelect' @@ -402,15 +405,21 @@ export abstract class UtxoBaseAdapter implements IChainAd } } - async signAndBroadcastTransaction(signTxInput: SignTxInput>): Promise { + async signAndBroadcastTransaction({ + from, + to, + signTxInput, + }: SignAndBroadcastTransactionInput): Promise { + await Promise.all([validateAddress(from), validateAddress(to)]) try { const { wallet } = signTxInput if (!supportsBTC(wallet)) { throw new Error(`UtxoBaseAdapter: wallet does not support ${this.coinName}`) } - const signedTx = await this.signTransaction(signTxInput) - return this.broadcastTransaction(signedTx) + const hex = await this.signTransaction(signTxInput) + + return this.broadcastTransaction({ from, to, hex }) } catch (err) { return ErrorHandler(err) } @@ -485,7 +494,8 @@ export abstract class UtxoBaseAdapter implements IChainAd } } - broadcastTransaction(hex: string): Promise { + async broadcastTransaction({ from, to, hex }: BroadcastTransactionInput): Promise { + await Promise.all([validateAddress(from), validateAddress(to)]) return this.providers.http.sendTx({ sendTxBody: { hex } }) } diff --git a/packages/chain-adapters/src/utxo/bitcoin/BitcoinChainAdapter.test.ts b/packages/chain-adapters/src/utxo/bitcoin/BitcoinChainAdapter.test.ts index a3ef7ee235e..f03690c98ac 100644 --- a/packages/chain-adapters/src/utxo/bitcoin/BitcoinChainAdapter.test.ts +++ b/packages/chain-adapters/src/utxo/bitcoin/BitcoinChainAdapter.test.ts @@ -16,6 +16,10 @@ import type { Account, BuildSendTxInput, GetFeeDataInput } from '../../types' import type { ChainAdapterArgs, UtxoChainId } from '../UtxoBaseAdapter' import * as bitcoin from './BitcoinChainAdapter' +jest.mock('../utils/validatePubkey', () => ({ + validatePubkey: jest.fn(), +})) + const testMnemonic = 'alcohol woman abuse must during monitor noble actual mixed trade anger aisle' const VALID_CHAIN_ID = 'bip122:000000000019d6689c085ae165831e93' const VALID_ASSET_ID = 'bip122:000000000019d6689c085ae165831e93/slip44:0' @@ -310,7 +314,11 @@ describe('BitcoinChainAdapter', () => { } as any const adapter = new bitcoin.ChainAdapter(args) const mockTx = '0x123' - const result = await adapter.broadcastTransaction(mockTx) + const result = await adapter.broadcastTransaction({ + from: '0x1234', + to: '0x1234', + hex: mockTx, + }) expect(args.providers.http.sendTx).toHaveBeenCalledWith({ sendTxBody: { hex: mockTx } }) expect(result).toEqual(sendDataResult) }) diff --git a/packages/chain-adapters/src/utxo/bitcoincash/BitcoinCashChainAdapter.test.ts b/packages/chain-adapters/src/utxo/bitcoincash/BitcoinCashChainAdapter.test.ts index 0d3da1e2a9b..1b94e016c12 100644 --- a/packages/chain-adapters/src/utxo/bitcoincash/BitcoinCashChainAdapter.test.ts +++ b/packages/chain-adapters/src/utxo/bitcoincash/BitcoinCashChainAdapter.test.ts @@ -22,6 +22,10 @@ import type { Account, BuildSendTxInput, GetFeeDataInput } from '../../types' import type { ChainAdapterArgs, UtxoChainId } from '../UtxoBaseAdapter' import * as bitcoincash from './BitcoinCashChainAdapter' +jest.mock('../utils/validatePubkey', () => ({ + validatePubkey: jest.fn(), +})) + const testMnemonic = 'alcohol woman abuse must during monitor noble actual mixed trade anger aisle' const VALID_CHAIN_ID = bchChainId const VALID_ASSET_ID = bchAssetId @@ -329,7 +333,11 @@ describe('BitcoinCashChainAdapter', () => { } as any const adapter = new bitcoincash.ChainAdapter(args) const mockTx = '0x123' - const result = await adapter.broadcastTransaction(mockTx) + const result = await adapter.broadcastTransaction({ + from: '0x1234', + to: '0x1234', + hex: mockTx, + }) expect(args.providers.http.sendTx).toHaveBeenCalledWith({ sendTxBody: { hex: mockTx } }) expect(result).toEqual(sendDataResult) }) diff --git a/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.test.ts b/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.test.ts index 442086b452c..30b63241741 100644 --- a/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.test.ts +++ b/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.test.ts @@ -16,6 +16,10 @@ import type { Account, BuildSendTxInput, GetFeeDataInput } from '../../types' import type { ChainAdapterArgs, UtxoChainId } from '../UtxoBaseAdapter' import * as dogecoin from './DogecoinChainAdapter' +jest.mock('../utils/validatePubkey', () => ({ + validatePubkey: jest.fn(), +})) + const testMnemonic = 'alcohol woman abuse must during monitor noble actual mixed trade anger aisle' const VALID_CHAIN_ID = 'bip122:00000000001a91e3dace36e2be3bf030' const VALID_ASSET_ID = 'bip122:00000000001a91e3dace36e2be3bf030/slip44:3' @@ -318,7 +322,11 @@ describe('DogecoinChainAdapter', () => { } as any const adapter = new dogecoin.ChainAdapter(args) const mockTx = '0x123' - const result = await adapter.broadcastTransaction(mockTx) + const result = await adapter.broadcastTransaction({ + from: '0x1234', + to: '0x1234', + hex: mockTx, + }) expect(args.providers.http.sendTx).toHaveBeenCalledWith({ sendTxBody: { hex: mockTx } }) expect(result).toEqual(sendDataResult) }) diff --git a/packages/chain-adapters/src/utxo/litecoin/LitecoinChainAdapter.test.ts b/packages/chain-adapters/src/utxo/litecoin/LitecoinChainAdapter.test.ts index 7ed78a37ade..a61e0cb03d2 100644 --- a/packages/chain-adapters/src/utxo/litecoin/LitecoinChainAdapter.test.ts +++ b/packages/chain-adapters/src/utxo/litecoin/LitecoinChainAdapter.test.ts @@ -17,6 +17,10 @@ import type { Account, BuildSendTxInput, GetFeeDataInput } from '../../types' import type { ChainAdapterArgs, UtxoChainId } from '../UtxoBaseAdapter' import * as litecoin from './LitecoinChainAdapter' +jest.mock('../utils/validatePubkey', () => ({ + validatePubkey: jest.fn(), +})) + const testMnemonic = 'alcohol woman abuse must during monitor noble actual mixed trade anger aisle' const VALID_CHAIN_ID = ltcChainId const VALID_ASSET_ID = ltcAssetId @@ -316,7 +320,11 @@ describe('LitecoinChainAdapter', () => { } as any const adapter = new litecoin.ChainAdapter(args) const mockTx = '0x123' - const result = await adapter.broadcastTransaction(mockTx) + const result = await adapter.broadcastTransaction({ + from: '0x1234', + to: '0x1234', + hex: mockTx, + }) expect(args.providers.http.sendTx).toHaveBeenCalledWith({ sendTxBody: { hex: mockTx } }) expect(result).toEqual(sendDataResult) }) diff --git a/src/components/Modals/Send/utils.ts b/src/components/Modals/Send/utils.ts index e0db4d925c7..68f04896fe5 100644 --- a/src/components/Modals/Send/utils.ts +++ b/src/components/Modals/Send/utils.ts @@ -1,20 +1,18 @@ import type { AssetId, ChainId } from '@shapeshiftoss/caip' import { CHAIN_NAMESPACE, fromAccountId, fromAssetId, fromChainId } from '@shapeshiftoss/caip' import type { + ChainAdapter, CosmosSdkBaseAdapter, CosmosSdkChainId, + EvmBaseAdapter, + EvmChainId, + FeeData, FeeDataEstimate, GetFeeDataInput, + UtxoBaseAdapter, + UtxoChainId, } from '@shapeshiftoss/chain-adapters' -import { - type ChainAdapter, - type EvmBaseAdapter, - type EvmChainId, - type FeeData, - type UtxoBaseAdapter, - type UtxoChainId, - utxoChainIds, -} from '@shapeshiftoss/chain-adapters' +import { utxoChainIds } from '@shapeshiftoss/chain-adapters' import type { HDWallet } from '@shapeshiftoss/hdwallet-core' import { supportsETH } from '@shapeshiftoss/hdwallet-core' import type { KnownChainIds } from '@shapeshiftoss/types' @@ -223,7 +221,7 @@ export const handleSend = async ({ txToSign, wallet, }) - return adapter.broadcastTransaction(signedTx) + return adapter.broadcastTransaction({ from, to, hex: signedTx }) } else if (wallet.supportsBroadcast()) { /** * signAndBroadcastTransaction is an optional method on the HDWallet interface. @@ -232,7 +230,7 @@ export const handleSend = async ({ if (!adapter.signAndBroadcastTransaction) { throw new Error('signAndBroadcastTransaction undefined for wallet') } - return adapter.signAndBroadcastTransaction?.({ txToSign, wallet }) + return adapter.signAndBroadcastTransaction({ from, to, signTxInput: { txToSign, wallet } }) } else { throw new Error('Bad hdwallet config') } diff --git a/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts b/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts index 083afe4f1eb..1334be318cc 100644 --- a/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts +++ b/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts @@ -151,7 +151,7 @@ export const useTradeExecution = ({ wallet, accountNumber, } as BuildCustomTxInput) - return await signAndBroadcast({ adapter, txToSign, wallet }) + return await signAndBroadcast({ adapter, txToSign, wallet, accountNumber }) }, }) cancelPollingRef.current = output?.cancelPolling @@ -173,7 +173,11 @@ export const useTradeExecution = ({ txToSign, wallet, }) - return adapter.broadcastTransaction(signedTx) + return adapter.broadcastTransaction({ + from: xpub, + to: tradeQuote.receiveAddress, + hex: signedTx, + }) }, }) cancelPollingRef.current = output?.cancelPolling @@ -208,7 +212,11 @@ export const useTradeExecution = ({ txToSign, wallet, }) - return adapter.broadcastTransaction(signedTx) + return adapter.broadcastTransaction({ + from, + to: tradeQuote.receiveAddress, + hex: signedTx, + }) }, }) cancelPollingRef.current = output?.cancelPolling diff --git a/src/context/PluginProvider/PluginProvider.tsx b/src/context/PluginProvider/PluginProvider.tsx index 136e5b264e8..d286a39c83b 100644 --- a/src/context/PluginProvider/PluginProvider.tsx +++ b/src/context/PluginProvider/PluginProvider.tsx @@ -62,7 +62,7 @@ export const PluginProvider = ({ children }: PluginProviderProps): JSX.Element = const newChainAdapters: { [k in ChainId]?: () => ChainAdapter } = {} // register providers from each plugin - for (const [, plugin] of pluginManager.entries()) { + for (const plugin of pluginManager.values()) { // Ignore plugins that have their feature flag disabled // If no featureFlag is present, then we assume it's enabled const featureFlagEnabled = diff --git a/src/lib/investor/investor-foxy/api/api.ts b/src/lib/investor/investor-foxy/api/api.ts index bb5ab8ebd0c..d6f21acf5be 100644 --- a/src/lib/investor/investor-foxy/api/api.ts +++ b/src/lib/investor/investor-foxy/api/api.ts @@ -142,6 +142,12 @@ export class FoxyApi { accountNumber: payload.bip44Params.accountNumber, ...(shouldUseEIP1559Fees ? { maxFeePerGas, maxPriorityFeePerGas } : { gasPrice }), }) + + const from = await this.adapter.getAddress({ + accountNumber: payload.bip44Params.accountNumber, + wallet, + }) + if (wallet.supportsOfflineSigning()) { const signedTx = await this.adapter.signTransaction({ txToSign, wallet }) if (dryRun) return signedTx @@ -150,7 +156,7 @@ export class FoxyApi { const sendSignedTx = await this.provider.sendTransaction(signedTx) return sendSignedTx?.blockHash ?? '' } - return this.adapter.broadcastTransaction(signedTx) + return this.adapter.broadcastTransaction({ from, to: payload.to, hex: signedTx }) } catch (e) { throw new Error(`Failed to broadcast: ${e}`) } @@ -158,7 +164,11 @@ export class FoxyApi { if (dryRun) { throw new Error(`Cannot perform a dry run with wallet of type ${wallet.getVendor()}`) } - return this.adapter.signAndBroadcastTransaction({ txToSign, wallet }) + return this.adapter.signAndBroadcastTransaction({ + from, + to: payload.to, + signTxInput: { txToSign, wallet }, + }) } else { throw new Error('Invalid HDWallet configuration ') } diff --git a/src/lib/investor/investor-idle/IdleOpportunity.ts b/src/lib/investor/investor-idle/IdleOpportunity.ts index b2ce4bb0dbd..b7cc74b1e8e 100644 --- a/src/lib/investor/investor-idle/IdleOpportunity.ts +++ b/src/lib/investor/investor-idle/IdleOpportunity.ts @@ -463,15 +463,24 @@ export class IdleOpportunity addressNList: toAddressNList(bip44Params), } + const from = await chainAdapter.getAddress({ + accountNumber: bip44Params.accountNumber, + wallet, + }) + if (wallet.supportsOfflineSigning()) { const signedTx = await chainAdapter.signTransaction({ txToSign, wallet }) if (this.#internals.dryRun) return signedTx - return chainAdapter.broadcastTransaction(signedTx) + return chainAdapter.broadcastTransaction({ from, to: tx.to, hex: signedTx }) } else if (wallet.supportsBroadcast() && chainAdapter.signAndBroadcastTransaction) { if (this.#internals.dryRun) { throw new Error(`Cannot perform a dry run with wallet of type ${wallet.getVendor()}`) } - return chainAdapter.signAndBroadcastTransaction({ txToSign, wallet }) + return chainAdapter.signAndBroadcastTransaction({ + from, + to: tx.to, + signTxInput: { txToSign, wallet }, + }) } else { throw new Error('Invalid HDWallet configuration') } diff --git a/src/lib/investor/investor-yearn/YearnOpportunity.ts b/src/lib/investor/investor-yearn/YearnOpportunity.ts index 4e4521314d8..2c81d3996e4 100644 --- a/src/lib/investor/investor-yearn/YearnOpportunity.ts +++ b/src/lib/investor/investor-yearn/YearnOpportunity.ts @@ -3,8 +3,7 @@ import type { ChainAdapter } from '@shapeshiftoss/chain-adapters' import { toAddressNList } from '@shapeshiftoss/chain-adapters' import type { ETHSignTx, HDWallet } from '@shapeshiftoss/hdwallet-core' import type { BIP44Params, KnownChainIds } from '@shapeshiftoss/types' -import type { Yearn } from '@yfi/sdk' -import { type ChainId, type Vault, type VaultMetadata } from '@yfi/sdk' +import type { ChainId, Vault, VaultMetadata, Yearn } from '@yfi/sdk' import type { BigNumber } from 'bignumber.js' import isNil from 'lodash/isNil' import toLower from 'lodash/toLower' @@ -196,15 +195,24 @@ export class YearnOpportunity addressNList: toAddressNList(bip44Params), } + const from = await chainAdapter.getAddress({ + accountNumber: bip44Params.accountNumber, + wallet, + }) + if (wallet.supportsOfflineSigning()) { const signedTx = await chainAdapter.signTransaction({ txToSign, wallet }) if (this.#internals.dryRun) return signedTx - return chainAdapter.broadcastTransaction(signedTx) + return chainAdapter.broadcastTransaction({ from, to: tx.to, hex: signedTx }) } else if (wallet.supportsBroadcast() && chainAdapter.signAndBroadcastTransaction) { if (this.#internals.dryRun) { throw new Error(`Cannot perform a dry run with wallet of type ${wallet.getVendor()}`) } - return chainAdapter.signAndBroadcastTransaction({ txToSign, wallet }) + return chainAdapter.signAndBroadcastTransaction({ + from, + to: tx.to, + signTxInput: { txToSign, wallet }, + }) } else { throw new Error('Invalid HDWallet configuration') } diff --git a/src/lib/swapper/types.ts b/src/lib/swapper/types.ts index 48f90ddbaae..3c30509f94e 100644 --- a/src/lib/swapper/types.ts +++ b/src/lib/swapper/types.ts @@ -215,6 +215,7 @@ export type GetUnsignedCosmosSdkTransactionArgs = CommonGetUnsignedTransactionAr export type UnsignedTx = Nominal, 'UnsignedTx'> export type ExecuteTradeArgs = { + accountNumber: number txToSign: UnsignedTx wallet: HDWallet chainId: ChainId diff --git a/src/lib/utils/evm.ts b/src/lib/utils/evm.ts index 1655932085b..105477b8101 100644 --- a/src/lib/utils/evm.ts +++ b/src/lib/utils/evm.ts @@ -34,12 +34,14 @@ type BuildArgs = { } type BroadcastArgs = { + accountNumber: number adapter: EvmChainAdapter txToSign: SignTx wallet: HDWallet } -type BuildAndBroadcastArgs = BuildArgs & Omit +type BuildAndBroadcastArgs = BuildArgs & + Omit type CreateBuildCustomTxInputArgs = { accountNumber: number @@ -169,20 +171,36 @@ export const createBuildCustomApiTxInput = async ( export const buildAndBroadcast = async ({ adapter, buildCustomTxInput }: BuildAndBroadcastArgs) => { const { txToSign } = await adapter.buildCustomTx(buildCustomTxInput) - return signAndBroadcast({ adapter, txToSign, wallet: buildCustomTxInput.wallet }) + return signAndBroadcast({ + adapter, + txToSign, + wallet: buildCustomTxInput.wallet, + accountNumber: buildCustomTxInput.accountNumber, + }) } -export const signAndBroadcast = async ({ adapter, txToSign, wallet }: BroadcastArgs) => { +export const signAndBroadcast = async ({ + adapter, + txToSign, + wallet, + accountNumber, +}: BroadcastArgs) => { if (!wallet) throw new Error('Wallet is required to broadcast EVM Txs') + const from = await adapter.getAddress({ accountNumber, wallet }) + if (wallet.supportsOfflineSigning()) { const signedTx = await adapter.signTransaction({ txToSign, wallet }) - const txid = await adapter.broadcastTransaction(signedTx) + const txid = await adapter.broadcastTransaction({ from, to: txToSign.to, hex: signedTx }) return txid } if (wallet.supportsBroadcast() && adapter.signAndBroadcastTransaction) { - const txid = await adapter.signAndBroadcastTransaction({ txToSign, wallet }) + const txid = await adapter.signAndBroadcastTransaction({ + from, + to: txToSign.to, + signTxInput: { txToSign, wallet }, + }) return txid } @@ -225,9 +243,9 @@ export const assertGetEvmChainAdapter = (chainId: ChainId | KnownChainIds): EvmC return adapter } -export const executeEvmTrade = ({ txToSign, wallet, chainId }: ExecuteTradeArgs) => { +export const executeEvmTrade = ({ txToSign, wallet, chainId, accountNumber }: ExecuteTradeArgs) => { const adapter = assertGetEvmChainAdapter(chainId) - return signAndBroadcast({ adapter, wallet, txToSign: txToSign as ETHSignTx }) + return signAndBroadcast({ adapter, wallet, txToSign: txToSign as ETHSignTx, accountNumber }) } export const executeEvmTransaction = ( diff --git a/src/plugins/cosmos/hooks/useStakingAction/useStakingAction.tsx b/src/plugins/cosmos/hooks/useStakingAction/useStakingAction.tsx index d4d61059cae..2f3982e3d45 100644 --- a/src/plugins/cosmos/hooks/useStakingAction/useStakingAction.tsx +++ b/src/plugins/cosmos/hooks/useStakingAction/useStakingAction.tsx @@ -60,8 +60,8 @@ export const useStakingAction = () => { const memo = shapeshiftValidators.includes(validator) ? 'Delegated with ShapeShift' : '' + const { accountNumber } = bip44Params const { txToSign } = await (() => { - const { accountNumber } = bip44Params switch (action) { case StakingAction.Claim: return adapter.buildClaimRewardsTransaction({ @@ -93,8 +93,12 @@ export const useStakingAction = () => { throw new Error(`unsupported staking action: ${action}`) } })() - - return adapter.signAndBroadcastTransaction({ txToSign, wallet }) + const from = await adapter.getAddress({ accountNumber, wallet }) + return adapter.signAndBroadcastTransaction({ + from, + to: validator, + signTxInput: { txToSign, wallet }, + }) } catch (error) { console.error(error) } diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 4505b7bd628..7143d6d01eb 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -20,4 +20,8 @@ export class PluginManager { keys() { return [...this.#pluginManager.keys()] } + + values() { + return [...this.#pluginManager.values()] + } } diff --git a/src/plugins/walletConnectToDapps/utils/EIP155RequestHandlerUtil.ts b/src/plugins/walletConnectToDapps/utils/EIP155RequestHandlerUtil.ts index a672badb4a0..9d0bed984e4 100644 --- a/src/plugins/walletConnectToDapps/utils/EIP155RequestHandlerUtil.ts +++ b/src/plugins/walletConnectToDapps/utils/EIP155RequestHandlerUtil.ts @@ -88,6 +88,7 @@ export const approveEIP155Request = async ({ const didUserChangeNonce = maybeAdvancedParamsNonce && maybeAdvancedParamsNonce !== sendTransaction.nonce const fees = await getFeesForTx(sendTransaction, chainAdapter, accountId) + const from = await chainAdapter.getAddress({ accountNumber, wallet }) const gasData = getGasData(customTransactionData, fees) const { txToSign: txToSignWithPossibleWrongNonce } = await chainAdapter.buildCustomTx({ wallet, @@ -107,7 +108,11 @@ export const approveEIP155Request = async ({ txToSign, wallet, }) - const txHash = await chainAdapter.broadcastTransaction(signedTx) + const txHash = await chainAdapter.broadcastTransaction({ + from, + to: txToSign.to, + hex: signedTx, + }) return formatJsonRpcResult(id, txHash) } From 0ead0fefa83633a3536049cd008e99a32b2f86c5 Mon Sep 17 00:00:00 2001 From: woodenfurniture <125113430+woodenfurniture@users.noreply.github.com> Date: Tue, 10 Oct 2023 14:42:30 +1100 Subject: [PATCH 02/12] chore: revert initial implementation except for csps --- .env.base | 2 -- src/config.ts | 1 - src/lib/swapper/swapper.ts | 19 ------------------- src/lib/swapper/utils.ts | 26 -------------------------- 4 files changed, 48 deletions(-) diff --git a/.env.base b/.env.base index 47830adf072..3500623f3c5 100644 --- a/.env.base +++ b/.env.base @@ -159,5 +159,3 @@ REACT_APP_EXPERIMENTAL_MM_SNAPPY_FINGERS=true # Experemental features (not production ready) REACT_APP_EXPERIMENTAL_CUSTOM_SEND_NONCE=false - -REACT_APP_TRM_LABS_API_URL=https://api.trmlabs.com/public/v1/sanctions/screening \ No newline at end of file diff --git a/src/config.ts b/src/config.ts index 755b6ff1603..27afdca9e34 100644 --- a/src/config.ts +++ b/src/config.ts @@ -153,7 +153,6 @@ const validators = { REACT_APP_EXPERIMENTAL_MM_SNAPPY_FINGERS: bool({ default: false }), REACT_APP_SNAP_ID: str(), REACT_APP_FEATURE_FOX_DISCOUNTS: bool({ default: false }), - REACT_APP_TRM_LABS_API_URL: url(), } function reporter({ errors }: envalid.ReporterOptions) { diff --git a/src/lib/swapper/swapper.ts b/src/lib/swapper/swapper.ts index f61b351ab02..ea4e7c73535 100644 --- a/src/lib/swapper/swapper.ts +++ b/src/lib/swapper/swapper.ts @@ -11,7 +11,6 @@ import type { SwapperName, TradeQuote, } from './types' -import { isValidSwapAddress } from './utils' export const getTradeQuotes = async ( getTradeQuoteInput: GetTradeQuoteInput, @@ -20,24 +19,6 @@ export const getTradeQuotes = async ( if (bnOrZero(getTradeQuoteInput.affiliateBps).lt(0)) return [] if (getTradeQuoteInput.sellAmountIncludingProtocolFeesCryptoBaseUnit === '0') return [] - try { - if (getTradeQuoteInput.sendAddress !== undefined) { - const isSenderValid = await isValidSwapAddress(getTradeQuoteInput.sendAddress) - if (!isSenderValid) { - return [] // no quotes available for this address - } - } - - if (getTradeQuoteInput.sendAddress !== getTradeQuoteInput.receiveAddress) { - const isReceiverValid = await isValidSwapAddress(getTradeQuoteInput.receiveAddress) - if (!isReceiverValid) { - return [] // no quotes available for this address - } - } - } catch (e) { - console.error(e) // log error but don't fail - } - const quotes = await Promise.allSettled( swappers .filter(({ swapperName }) => enabledSwappers.includes(swapperName)) diff --git a/src/lib/swapper/utils.ts b/src/lib/swapper/utils.ts index 40c2dd53928..aea368f58b1 100644 --- a/src/lib/swapper/utils.ts +++ b/src/lib/swapper/utils.ts @@ -2,10 +2,8 @@ import type { AssetId } from '@shapeshiftoss/caip' import type { Result } from '@sniptt/monads' import { Err, Ok } from '@sniptt/monads' import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios' -import axios from 'axios' import type { ISetupCache } from 'axios-cache-adapter' import { setupCache } from 'axios-cache-adapter' -import { getConfig } from 'config' import type { FeatureFlags } from 'state/slices/preferencesSlice/preferencesSlice' import { isCrossAccountTradeSupported } from '../../state/helpers' @@ -135,27 +133,3 @@ export const createTradeAmountTooSmallErr = (details?: { message: 'Sell amount is too small', details, }) - -export const isValidSwapAddress = async (address: string): Promise => { - type trmResponse = [ - { - address: string - isSanctioned: boolean - }, - ] - - const response = await axios.post( - getConfig().REACT_APP_TRM_LABS_API_URL, - [ - { - address, - }, - ], - { - headers: { - Accept: 'application/json', - }, - }, - ) - return !response.data[0].isSanctioned -} From 80d2ac1dbaebb76e6da6ac291de41d7fe4d378be Mon Sep 17 00:00:00 2001 From: woodenfurniture <125113430+woodenfurniture@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:22:34 +1100 Subject: [PATCH 03/12] fix: contracts aren't receivers --- .../src/cosmossdk/CosmosSdkBaseAdapter.ts | 17 ++++--- .../cosmossdk/cosmos/CosmosChainAdapter.ts | 9 ++-- .../thorchain/ThorchainChainAdapter.ts | 9 ++-- .../chain-adapters/src/evm/EvmBaseAdapter.ts | 20 ++++++-- .../evm/arbitrum/ArbitrumChainAdapter.test.ts | 16 +++++-- .../avalanche/AvalancheChainAdapter.test.ts | 16 +++++-- .../evm/bnbsmartchain/BscChainAdapter.test.ts | 16 +++++-- .../evm/ethereum/EthereumChainAdapter.test.ts | 16 +++++-- .../src/evm/gnosis/GnosisChainAdapter.test.ts | 16 +++++-- .../evm/optimism/OptimismChainAdapter.test.ts | 16 +++++-- .../evm/polygon/PolygonChainAdapter.test.ts | 16 +++++-- packages/chain-adapters/src/types.ts | 8 ++-- .../src/utxo/UtxoBaseAdapter.ts | 24 +++++++--- .../utxo/bitcoin/BitcoinChainAdapter.test.ts | 4 +- .../BitcoinCashChainAdapter.test.ts | 4 +- .../dogecoin/DogecoinChainAdapter.test.ts | 4 +- .../litecoin/LitecoinChainAdapter.test.ts | 4 +- src/components/Modals/Send/utils.ts | 12 ++++- .../hooks/useExecuteAllowanceApproval.tsx | 6 ++- .../useTradeExecution/useTradeExecution.ts | 16 +++++-- .../fox-farming/hooks/useFoxFarming.ts | 19 ++++++-- .../Deposit/components/Approve.tsx | 6 ++- .../Deposit/components/Confirm.tsx | 10 ++-- .../Withdraw/components/Confirm.tsx | 10 ++-- .../univ2/hooks/useUniV2LiquidityPool.ts | 13 ++++- src/lib/investor/investor-foxy/api/api.ts | 12 +++-- .../investor/investor-idle/IdleOpportunity.ts | 12 +++-- .../investor-yearn/YearnOpportunity.ts | 12 +++-- src/lib/swapper/types.ts | 3 +- src/lib/utils/evm.ts | 48 ++++++++++++++----- .../useStakingAction/useStakingAction.tsx | 6 +-- .../utils/EIP155RequestHandlerUtil.ts | 6 +-- 32 files changed, 291 insertions(+), 115 deletions(-) diff --git a/packages/chain-adapters/src/cosmossdk/CosmosSdkBaseAdapter.ts b/packages/chain-adapters/src/cosmossdk/CosmosSdkBaseAdapter.ts index ba5aa7cd98d..c4ece07786f 100644 --- a/packages/chain-adapters/src/cosmossdk/CosmosSdkBaseAdapter.ts +++ b/packages/chain-adapters/src/cosmossdk/CosmosSdkBaseAdapter.ts @@ -155,11 +155,7 @@ export abstract class CosmosSdkBaseAdapter implement abstract getAddress(input: GetAddressInput): Promise abstract getFeeData(input: Partial>): Promise> abstract signTransaction(signTxInput: SignTxInput>): Promise - abstract signAndBroadcastTransaction({ - from, - to, - signTxInput, - }: SignAndBroadcastTransactionInput): Promise + abstract signAndBroadcastTransaction(input: SignAndBroadcastTransactionInput): Promise getChainId(): ChainId { return this.chainId @@ -336,8 +332,15 @@ export abstract class CosmosSdkBaseAdapter implement return { txToSign } } - async broadcastTransaction({ from, to, hex }: BroadcastTransactionInput): Promise { - await Promise.all([validateAddress(from), validateAddress(to)]) + async broadcastTransaction({ + senderAddress, + receiverAddress, + hex, + }: BroadcastTransactionInput): Promise { + await Promise.all([ + validateAddress(senderAddress), + receiverAddress !== undefined && validateAddress(receiverAddress), + ]) try { return this.providers.http.sendTx({ body: { rawTx: hex } }) diff --git a/packages/chain-adapters/src/cosmossdk/cosmos/CosmosChainAdapter.ts b/packages/chain-adapters/src/cosmossdk/cosmos/CosmosChainAdapter.ts index c7e85b42efe..43a2da55700 100644 --- a/packages/chain-adapters/src/cosmossdk/cosmos/CosmosChainAdapter.ts +++ b/packages/chain-adapters/src/cosmossdk/cosmos/CosmosChainAdapter.ts @@ -283,11 +283,14 @@ export class ChainAdapter extends CosmosSdkBaseAdapter): Promise { - await Promise.all([validateAddress(from), validateAddress(to)]) + await Promise.all([ + validateAddress(senderAddress), + receiverAddress !== undefined && validateAddress(receiverAddress), + ]) const { wallet } = signTxInput try { diff --git a/packages/chain-adapters/src/cosmossdk/thorchain/ThorchainChainAdapter.ts b/packages/chain-adapters/src/cosmossdk/thorchain/ThorchainChainAdapter.ts index cfec6ffed4b..d6ab58294e5 100644 --- a/packages/chain-adapters/src/cosmossdk/thorchain/ThorchainChainAdapter.ts +++ b/packages/chain-adapters/src/cosmossdk/thorchain/ThorchainChainAdapter.ts @@ -207,11 +207,14 @@ export class ChainAdapter extends CosmosSdkBaseAdapter): Promise { - await Promise.all([validateAddress(from), validateAddress(to)]) + await Promise.all([ + validateAddress(senderAddress), + receiverAddress !== undefined && validateAddress(receiverAddress), + ]) const { wallet } = signTxInput diff --git a/packages/chain-adapters/src/evm/EvmBaseAdapter.ts b/packages/chain-adapters/src/evm/EvmBaseAdapter.ts index 3575411211d..776d3c09fb5 100644 --- a/packages/chain-adapters/src/evm/EvmBaseAdapter.ts +++ b/packages/chain-adapters/src/evm/EvmBaseAdapter.ts @@ -446,11 +446,14 @@ export abstract class EvmBaseAdapter implements IChainAdap } async signAndBroadcastTransaction({ - from, - to, + senderAddress, + receiverAddress, signTxInput, }: SignAndBroadcastTransactionInput): Promise { - await Promise.all([validateAddress(from), validateAddress(to)]) + await Promise.all([ + validateAddress(senderAddress), + receiverAddress !== undefined && validateAddress(receiverAddress), + ]) try { const { txToSign, wallet } = signTxInput @@ -470,8 +473,15 @@ export abstract class EvmBaseAdapter implements IChainAdap } } - async broadcastTransaction({ from, to, hex }: BroadcastTransactionInput): Promise { - await Promise.all([validateAddress(from), validateAddress(to)]) + async broadcastTransaction({ + senderAddress, + receiverAddress, + hex, + }: BroadcastTransactionInput): Promise { + await Promise.all([ + validateAddress(senderAddress), + receiverAddress !== undefined && validateAddress(receiverAddress), + ]) return this.providers.http.sendTx({ sendTxBody: { hex } }) } diff --git a/packages/chain-adapters/src/evm/arbitrum/ArbitrumChainAdapter.test.ts b/packages/chain-adapters/src/evm/arbitrum/ArbitrumChainAdapter.test.ts index 36869065d90..c59359bf88d 100644 --- a/packages/chain-adapters/src/evm/arbitrum/ArbitrumChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/arbitrum/ArbitrumChainAdapter.test.ts @@ -325,7 +325,11 @@ describe('ArbitrumChainAdapter', () => { const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput await expect( - adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + adapter.signAndBroadcastTransaction({ + senderAddress: '0x1234', + receiverAddress: '0x1234', + signTxInput, + }), ).rejects.toThrow(/Error signing & broadcasting tx/) }) @@ -341,7 +345,11 @@ describe('ArbitrumChainAdapter', () => { const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput await expect( - adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + adapter.signAndBroadcastTransaction({ + senderAddress: '0x1234', + receiverAddress: '0x1234', + signTxInput, + }), ).resolves.toEqual('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331') }) }) @@ -397,8 +405,8 @@ describe('ArbitrumChainAdapter', () => { const mockTx = '0x123' const result = await adapter.broadcastTransaction({ - from: '0x1234', - to: '0x1234', + senderAddress: '0x1234', + receiverAddress: '0x1234', hex: mockTx, }) diff --git a/packages/chain-adapters/src/evm/avalanche/AvalancheChainAdapter.test.ts b/packages/chain-adapters/src/evm/avalanche/AvalancheChainAdapter.test.ts index 236f9d34253..18756ce9295 100644 --- a/packages/chain-adapters/src/evm/avalanche/AvalancheChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/avalanche/AvalancheChainAdapter.test.ts @@ -330,7 +330,11 @@ describe('AvalancheChainAdapter', () => { const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput await expect( - adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + adapter.signAndBroadcastTransaction({ + senderAddress: '0x1234', + receiverAddress: '0x1234', + signTxInput, + }), ).rejects.toThrow(/Error signing & broadcasting tx/) }) @@ -346,7 +350,11 @@ describe('AvalancheChainAdapter', () => { const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput await expect( - adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + adapter.signAndBroadcastTransaction({ + senderAddress: '0x1234', + receiverAddress: '0x1234', + signTxInput, + }), ).resolves.toEqual('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331') }) }) @@ -402,8 +410,8 @@ describe('AvalancheChainAdapter', () => { const mockTx = '0x123' const result = await adapter.broadcastTransaction({ - from: '0x1234', - to: '0x1234', + senderAddress: '0x1234', + receiverAddress: '0x1234', hex: mockTx, }) diff --git a/packages/chain-adapters/src/evm/bnbsmartchain/BscChainAdapter.test.ts b/packages/chain-adapters/src/evm/bnbsmartchain/BscChainAdapter.test.ts index 1be6eb486a7..7a1bf5dd96f 100644 --- a/packages/chain-adapters/src/evm/bnbsmartchain/BscChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/bnbsmartchain/BscChainAdapter.test.ts @@ -307,7 +307,11 @@ describe('BscChainAdapter', () => { const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput await expect( - adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + adapter.signAndBroadcastTransaction({ + senderAddress: '0x1234', + receiverAddress: '0x1234', + signTxInput, + }), ).rejects.toThrow(/Error signing & broadcasting tx/) }) @@ -323,7 +327,11 @@ describe('BscChainAdapter', () => { const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput await expect( - adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + adapter.signAndBroadcastTransaction({ + senderAddress: '0x1234', + receiverAddress: '0x1234', + signTxInput, + }), ).resolves.toEqual('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331') }) }) @@ -379,8 +387,8 @@ describe('BscChainAdapter', () => { const mockTx = '0x123' const result = await adapter.broadcastTransaction({ - from: '0x1234', - to: '0x1234', + senderAddress: '0x1234', + receiverAddress: '0x1234', hex: mockTx, }) diff --git a/packages/chain-adapters/src/evm/ethereum/EthereumChainAdapter.test.ts b/packages/chain-adapters/src/evm/ethereum/EthereumChainAdapter.test.ts index 4dae9c387ec..92b0cb06b69 100644 --- a/packages/chain-adapters/src/evm/ethereum/EthereumChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/ethereum/EthereumChainAdapter.test.ts @@ -364,7 +364,11 @@ describe('EthereumChainAdapter', () => { const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput await expect( - adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + adapter.signAndBroadcastTransaction({ + senderAddress: '0x1234', + receiverAddress: '0x1234', + signTxInput, + }), ).rejects.toThrow(/Error signing & broadcasting tx/) }) @@ -380,7 +384,11 @@ describe('EthereumChainAdapter', () => { const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput await expect( - adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + adapter.signAndBroadcastTransaction({ + senderAddress: '0x1234', + receiverAddress: '0x1234', + signTxInput, + }), ).resolves.toEqual('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331') }) }) @@ -436,8 +444,8 @@ describe('EthereumChainAdapter', () => { const mockTx = '0x123' const result = await adapter.broadcastTransaction({ - from: '0x1234', - to: '0x1234', + senderAddress: '0x1234', + receiverAddress: '0x1234', hex: mockTx, }) diff --git a/packages/chain-adapters/src/evm/gnosis/GnosisChainAdapter.test.ts b/packages/chain-adapters/src/evm/gnosis/GnosisChainAdapter.test.ts index cba9cb9ee30..734ab3edc2e 100644 --- a/packages/chain-adapters/src/evm/gnosis/GnosisChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/gnosis/GnosisChainAdapter.test.ts @@ -325,7 +325,11 @@ describe('GnosisChainAdapter', () => { const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput await expect( - adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + adapter.signAndBroadcastTransaction({ + senderAddress: '0x1234', + receiverAddress: '0x1234', + signTxInput, + }), ).rejects.toThrow(/Error signing & broadcasting tx/) }) @@ -341,7 +345,11 @@ describe('GnosisChainAdapter', () => { const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput await expect( - adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + adapter.signAndBroadcastTransaction({ + senderAddress: '0x1234', + receiverAddress: '0x1234', + signTxInput, + }), ).resolves.toEqual('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331') }) }) @@ -397,8 +405,8 @@ describe('GnosisChainAdapter', () => { const mockTx = '0x123' const result = await adapter.broadcastTransaction({ - from: '0x1234', - to: '0x1234', + senderAddress: '0x1234', + receiverAddress: '0x1234', hex: mockTx, }) diff --git a/packages/chain-adapters/src/evm/optimism/OptimismChainAdapter.test.ts b/packages/chain-adapters/src/evm/optimism/OptimismChainAdapter.test.ts index f27c0f42ca1..11bd4d20b05 100644 --- a/packages/chain-adapters/src/evm/optimism/OptimismChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/optimism/OptimismChainAdapter.test.ts @@ -332,7 +332,11 @@ describe('OptimismChainAdapter', () => { const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput await expect( - adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + adapter.signAndBroadcastTransaction({ + senderAddress: '0x1234', + receiverAddress: '0x1234', + signTxInput, + }), ).rejects.toThrow(/Error signing & broadcasting tx/) }) @@ -348,7 +352,11 @@ describe('OptimismChainAdapter', () => { const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput await expect( - adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + adapter.signAndBroadcastTransaction({ + senderAddress: '0x1234', + receiverAddress: '0x1234', + signTxInput, + }), ).resolves.toEqual('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331') }) }) @@ -404,8 +412,8 @@ describe('OptimismChainAdapter', () => { const mockTx = '0x123' const result = await adapter.broadcastTransaction({ - from: '0x1234', - to: '0x1234', + senderAddress: '0x1234', + receiverAddress: '0x1234', hex: mockTx, }) diff --git a/packages/chain-adapters/src/evm/polygon/PolygonChainAdapter.test.ts b/packages/chain-adapters/src/evm/polygon/PolygonChainAdapter.test.ts index d9d9969c4f5..1e69e3853b3 100644 --- a/packages/chain-adapters/src/evm/polygon/PolygonChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/polygon/PolygonChainAdapter.test.ts @@ -325,7 +325,11 @@ describe('PolygonChainAdapter', () => { const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput await expect( - adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + adapter.signAndBroadcastTransaction({ + senderAddress: '0x1234', + receiverAddress: '0x1234', + signTxInput, + }), ).rejects.toThrow(/Error signing & broadcasting tx/) }) @@ -341,7 +345,11 @@ describe('PolygonChainAdapter', () => { const signTxInput = { wallet, txToSign: {} } as unknown as SignTxInput await expect( - adapter.signAndBroadcastTransaction({ from: '0x1234', to: '0x1234', signTxInput }), + adapter.signAndBroadcastTransaction({ + senderAddress: '0x1234', + receiverAddress: '0x1234', + signTxInput, + }), ).resolves.toEqual('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331') }) }) @@ -397,8 +405,8 @@ describe('PolygonChainAdapter', () => { const mockTx = '0x123' const result = await adapter.broadcastTransaction({ - from: '0x1234', - to: '0x1234', + senderAddress: '0x1234', + receiverAddress: '0x1234', hex: mockTx, }) diff --git a/packages/chain-adapters/src/types.ts b/packages/chain-adapters/src/types.ts index adec75bb28f..ab86d29d66c 100644 --- a/packages/chain-adapters/src/types.ts +++ b/packages/chain-adapters/src/types.ts @@ -318,13 +318,13 @@ export enum ChainAdapterDisplayName { } export type BroadcastTransactionInput = { - from: string - to: string + senderAddress: string + receiverAddress: string | undefined // this is not defined for staking etc hex: string } export type SignAndBroadcastTransactionInput = { - from: string - to: string + senderAddress: string + receiverAddress: string | undefined // this is not defined for staking etc signTxInput: SignTxInput> } diff --git a/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts b/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts index 298b9aaf785..954c58e47c9 100644 --- a/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts +++ b/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts @@ -406,11 +406,15 @@ export abstract class UtxoBaseAdapter implements IChainAd } async signAndBroadcastTransaction({ - from, - to, + senderAddress, + receiverAddress, signTxInput, }: SignAndBroadcastTransactionInput): Promise { - await Promise.all([validateAddress(from), validateAddress(to)]) + await Promise.all([ + validateAddress(senderAddress), + receiverAddress !== undefined && validateAddress(receiverAddress), + ]) + try { const { wallet } = signTxInput @@ -419,7 +423,7 @@ export abstract class UtxoBaseAdapter implements IChainAd } const hex = await this.signTransaction(signTxInput) - return this.broadcastTransaction({ from, to, hex }) + return this.broadcastTransaction({ senderAddress, receiverAddress, hex }) } catch (err) { return ErrorHandler(err) } @@ -494,8 +498,16 @@ export abstract class UtxoBaseAdapter implements IChainAd } } - async broadcastTransaction({ from, to, hex }: BroadcastTransactionInput): Promise { - await Promise.all([validateAddress(from), validateAddress(to)]) + async broadcastTransaction({ + senderAddress, + receiverAddress, + hex, + }: BroadcastTransactionInput): Promise { + await Promise.all([ + validateAddress(senderAddress), + receiverAddress !== undefined && validateAddress(receiverAddress), + ]) + return this.providers.http.sendTx({ sendTxBody: { hex } }) } diff --git a/packages/chain-adapters/src/utxo/bitcoin/BitcoinChainAdapter.test.ts b/packages/chain-adapters/src/utxo/bitcoin/BitcoinChainAdapter.test.ts index f03690c98ac..62cfdac8700 100644 --- a/packages/chain-adapters/src/utxo/bitcoin/BitcoinChainAdapter.test.ts +++ b/packages/chain-adapters/src/utxo/bitcoin/BitcoinChainAdapter.test.ts @@ -315,8 +315,8 @@ describe('BitcoinChainAdapter', () => { const adapter = new bitcoin.ChainAdapter(args) const mockTx = '0x123' const result = await adapter.broadcastTransaction({ - from: '0x1234', - to: '0x1234', + senderAddress: '0x1234', + receiverAddress: '0x1234', hex: mockTx, }) expect(args.providers.http.sendTx).toHaveBeenCalledWith({ sendTxBody: { hex: mockTx } }) diff --git a/packages/chain-adapters/src/utxo/bitcoincash/BitcoinCashChainAdapter.test.ts b/packages/chain-adapters/src/utxo/bitcoincash/BitcoinCashChainAdapter.test.ts index 1b94e016c12..b99efbe0b33 100644 --- a/packages/chain-adapters/src/utxo/bitcoincash/BitcoinCashChainAdapter.test.ts +++ b/packages/chain-adapters/src/utxo/bitcoincash/BitcoinCashChainAdapter.test.ts @@ -334,8 +334,8 @@ describe('BitcoinCashChainAdapter', () => { const adapter = new bitcoincash.ChainAdapter(args) const mockTx = '0x123' const result = await adapter.broadcastTransaction({ - from: '0x1234', - to: '0x1234', + senderAddress: '0x1234', + receiverAddress: '0x1234', hex: mockTx, }) expect(args.providers.http.sendTx).toHaveBeenCalledWith({ sendTxBody: { hex: mockTx } }) diff --git a/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.test.ts b/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.test.ts index 30b63241741..7e17314758a 100644 --- a/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.test.ts +++ b/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.test.ts @@ -323,8 +323,8 @@ describe('DogecoinChainAdapter', () => { const adapter = new dogecoin.ChainAdapter(args) const mockTx = '0x123' const result = await adapter.broadcastTransaction({ - from: '0x1234', - to: '0x1234', + senderAddress: '0x1234', + receiverAddress: '0x1234', hex: mockTx, }) expect(args.providers.http.sendTx).toHaveBeenCalledWith({ sendTxBody: { hex: mockTx } }) diff --git a/packages/chain-adapters/src/utxo/litecoin/LitecoinChainAdapter.test.ts b/packages/chain-adapters/src/utxo/litecoin/LitecoinChainAdapter.test.ts index a61e0cb03d2..1907e39a0db 100644 --- a/packages/chain-adapters/src/utxo/litecoin/LitecoinChainAdapter.test.ts +++ b/packages/chain-adapters/src/utxo/litecoin/LitecoinChainAdapter.test.ts @@ -321,8 +321,8 @@ describe('LitecoinChainAdapter', () => { const adapter = new litecoin.ChainAdapter(args) const mockTx = '0x123' const result = await adapter.broadcastTransaction({ - from: '0x1234', - to: '0x1234', + senderAddress: '0x1234', + receiverAddress: '0x1234', hex: mockTx, }) expect(args.providers.http.sendTx).toHaveBeenCalledWith({ sendTxBody: { hex: mockTx } }) diff --git a/src/components/Modals/Send/utils.ts b/src/components/Modals/Send/utils.ts index 68f04896fe5..6b3b2bf4d2e 100644 --- a/src/components/Modals/Send/utils.ts +++ b/src/components/Modals/Send/utils.ts @@ -221,7 +221,11 @@ export const handleSend = async ({ txToSign, wallet, }) - return adapter.broadcastTransaction({ from, to, hex: signedTx }) + return adapter.broadcastTransaction({ + senderAddress: from, + receiverAddress: to, + hex: signedTx, + }) } else if (wallet.supportsBroadcast()) { /** * signAndBroadcastTransaction is an optional method on the HDWallet interface. @@ -230,7 +234,11 @@ export const handleSend = async ({ if (!adapter.signAndBroadcastTransaction) { throw new Error('signAndBroadcastTransaction undefined for wallet') } - return adapter.signAndBroadcastTransaction({ from, to, signTxInput: { txToSign, wallet } }) + return adapter.signAndBroadcastTransaction({ + senderAddress: from, + receiverAddress: to, + signTxInput: { txToSign, wallet }, + }) } else { throw new Error('Bad hdwallet config') } diff --git a/src/components/MultiHopTrade/hooks/useAllowanceApproval/hooks/useExecuteAllowanceApproval.tsx b/src/components/MultiHopTrade/hooks/useAllowanceApproval/hooks/useExecuteAllowanceApproval.tsx index f4a17312338..630e4e8b149 100644 --- a/src/components/MultiHopTrade/hooks/useAllowanceApproval/hooks/useExecuteAllowanceApproval.tsx +++ b/src/components/MultiHopTrade/hooks/useAllowanceApproval/hooks/useExecuteAllowanceApproval.tsx @@ -36,7 +36,11 @@ export const useExecuteAllowanceApproval = ( } try { - const txId = await buildAndBroadcast({ buildCustomTxInput, adapter }) + const txId = await buildAndBroadcast({ + adapter, + buildCustomTxInput, + receiverAddress: undefined, // no receiver for this contract call + }) setTxId(txId) diff --git a/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts b/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts index 1334be318cc..b5887d0d9b9 100644 --- a/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts +++ b/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts @@ -151,7 +151,13 @@ export const useTradeExecution = ({ wallet, accountNumber, } as BuildCustomTxInput) - return await signAndBroadcast({ adapter, txToSign, wallet, accountNumber }) + return await signAndBroadcast({ + adapter, + txToSign, + wallet, + senderAddress: from, + receiverAddress: tradeQuote.receiveAddress, + }) }, }) cancelPollingRef.current = output?.cancelPolling @@ -174,8 +180,8 @@ export const useTradeExecution = ({ wallet, }) return adapter.broadcastTransaction({ - from: xpub, - to: tradeQuote.receiveAddress, + senderAddress: xpub, + receiverAddress: tradeQuote.receiveAddress, hex: signedTx, }) }, @@ -213,8 +219,8 @@ export const useTradeExecution = ({ wallet, }) return adapter.broadcastTransaction({ - from, - to: tradeQuote.receiveAddress, + senderAddress: from, + receiverAddress: tradeQuote.receiveAddress, hex: signedTx, }) }, diff --git a/src/features/defi/providers/fox-farming/hooks/useFoxFarming.ts b/src/features/defi/providers/fox-farming/hooks/useFoxFarming.ts index 73dd8d5aa0a..919eab946d5 100644 --- a/src/features/defi/providers/fox-farming/hooks/useFoxFarming.ts +++ b/src/features/defi/providers/fox-farming/hooks/useFoxFarming.ts @@ -74,7 +74,11 @@ export const useFoxFarming = ( wallet, }) - const txid = await buildAndBroadcast({ adapter, buildCustomTxInput }) + const txid = await buildAndBroadcast({ + adapter, + buildCustomTxInput, + receiverAddress: undefined, // no receiver for this contract call + }) return txid } catch (err) { @@ -115,7 +119,11 @@ export const useFoxFarming = ( wallet, }) - const txid = await buildAndBroadcast({ adapter, buildCustomTxInput }) + const txid = await buildAndBroadcast({ + adapter, + buildCustomTxInput, + receiverAddress: undefined, // no receiver for this contract call + }) return txid } catch (err) { @@ -236,6 +244,7 @@ export const useFoxFarming = ( const txid = await buildAndBroadcast({ adapter, + receiverAddress: undefined, // no receiver for this contract call buildCustomTxInput: { accountNumber, to: uniV2LPContract.address, @@ -265,7 +274,11 @@ export const useFoxFarming = ( wallet, }) - const txid = await buildAndBroadcast({ adapter, buildCustomTxInput }) + const txid = await buildAndBroadcast({ + adapter, + buildCustomTxInput, + receiverAddress: undefined, // no receiver for this contract call + }) return txid }, [accountNumber, adapter, ethAsset.chainId, contractAddress, foxFarmingContract, skip, wallet]) diff --git a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Approve.tsx b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Approve.tsx index 61a0322f514..9533cb1c858 100644 --- a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Approve.tsx +++ b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Approve.tsx @@ -161,7 +161,11 @@ export const Approve: React.FC = ({ accountId, onNext }) => { wallet, }) - await buildAndBroadcast({ adapter, buildCustomTxInput }) + await buildAndBroadcast({ + adapter, + buildCustomTxInput, + receiverAddress: undefined, // no receiver for this contract call + }) await poll({ fn: () => getErc20Allowance({ diff --git a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Confirm.tsx b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Confirm.tsx index 8480602f67c..4416b24f1c9 100644 --- a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Confirm.tsx +++ b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Confirm.tsx @@ -557,15 +557,19 @@ export const Confirm: React.FC = ({ accountId, onNext }) => { ]) const handleCustomTx = useCallback(async (): Promise => { - if (!wallet) return + if (!wallet || accountNumber === undefined) return const buildCustomTxInput = await getCustomTxInput() if (!buildCustomTxInput) return const adapter = assertGetEvmChainAdapter(chainId) - const txid = await buildAndBroadcast({ adapter, buildCustomTxInput }) + const txid = await buildAndBroadcast({ + adapter, + buildCustomTxInput, + receiverAddress: undefined, // no receiver for this contract call + }) return txid - }, [wallet, getCustomTxInput, chainId]) + }, [wallet, accountNumber, getCustomTxInput, chainId]) const handleMultiTxSend = useCallback(async (): Promise => { if (!wallet) return diff --git a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Withdraw/components/Confirm.tsx b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Withdraw/components/Confirm.tsx index f6449553708..f18f12ccfa9 100644 --- a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Withdraw/components/Confirm.tsx +++ b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Withdraw/components/Confirm.tsx @@ -609,15 +609,19 @@ export const Confirm: React.FC = ({ accountId, onNext }) => { ]) const handleCustomTx = useCallback(async (): Promise => { - if (!wallet) return + if (!wallet || accountNumber === undefined) return const buildCustomTxInput = await getCustomTxInput() if (!buildCustomTxInput) return const adapter = assertGetEvmChainAdapter(chainId) - const txid = await buildAndBroadcast({ adapter, buildCustomTxInput }) + const txid = await buildAndBroadcast({ + adapter, + buildCustomTxInput, + receiverAddress: undefined, // no receiver for this contract call + }) return txid - }, [wallet, getCustomTxInput, chainId]) + }, [wallet, accountNumber, getCustomTxInput, chainId]) const handleMultiTxSend = useCallback(async (): Promise => { if (!wallet) return diff --git a/src/features/defi/providers/univ2/hooks/useUniV2LiquidityPool.ts b/src/features/defi/providers/univ2/hooks/useUniV2LiquidityPool.ts index bec951f229c..4541e9901f0 100644 --- a/src/features/defi/providers/univ2/hooks/useUniV2LiquidityPool.ts +++ b/src/features/defi/providers/univ2/hooks/useUniV2LiquidityPool.ts @@ -199,7 +199,11 @@ export const useUniV2LiquidityPool = ({ wallet, }) - const txid = await buildAndBroadcast({ adapter, buildCustomTxInput }) + const txid = await buildAndBroadcast({ + adapter, + buildCustomTxInput, + receiverAddress: undefined, // no receiver + }) return txid } catch (err) { @@ -309,7 +313,11 @@ export const useUniV2LiquidityPool = ({ wallet, }) - const txid = await buildAndBroadcast({ adapter, buildCustomTxInput }) + const txid = await buildAndBroadcast({ + adapter, + buildCustomTxInput, + receiverAddress: undefined, // no receiver + }) return txid } catch (err) { @@ -577,6 +585,7 @@ export const useUniV2LiquidityPool = ({ const txid = await buildAndBroadcast({ adapter, + receiverAddress: undefined, // no receiver buildCustomTxInput: { accountNumber, to: contractAddress, diff --git a/src/lib/investor/investor-foxy/api/api.ts b/src/lib/investor/investor-foxy/api/api.ts index d6f21acf5be..5205a63a638 100644 --- a/src/lib/investor/investor-foxy/api/api.ts +++ b/src/lib/investor/investor-foxy/api/api.ts @@ -143,7 +143,7 @@ export class FoxyApi { ...(shouldUseEIP1559Fees ? { maxFeePerGas, maxPriorityFeePerGas } : { gasPrice }), }) - const from = await this.adapter.getAddress({ + const senderAddress = await this.adapter.getAddress({ accountNumber: payload.bip44Params.accountNumber, wallet, }) @@ -156,7 +156,11 @@ export class FoxyApi { const sendSignedTx = await this.provider.sendTransaction(signedTx) return sendSignedTx?.blockHash ?? '' } - return this.adapter.broadcastTransaction({ from, to: payload.to, hex: signedTx }) + return this.adapter.broadcastTransaction({ + senderAddress, + receiverAddress: undefined, // no receiver for this contract call + hex: signedTx, + }) } catch (e) { throw new Error(`Failed to broadcast: ${e}`) } @@ -165,8 +169,8 @@ export class FoxyApi { throw new Error(`Cannot perform a dry run with wallet of type ${wallet.getVendor()}`) } return this.adapter.signAndBroadcastTransaction({ - from, - to: payload.to, + senderAddress, + receiverAddress: undefined, // no receiver for this contract call signTxInput: { txToSign, wallet }, }) } else { diff --git a/src/lib/investor/investor-idle/IdleOpportunity.ts b/src/lib/investor/investor-idle/IdleOpportunity.ts index b7cc74b1e8e..44ab5e3e6fc 100644 --- a/src/lib/investor/investor-idle/IdleOpportunity.ts +++ b/src/lib/investor/investor-idle/IdleOpportunity.ts @@ -463,7 +463,7 @@ export class IdleOpportunity addressNList: toAddressNList(bip44Params), } - const from = await chainAdapter.getAddress({ + const senderAddress = await chainAdapter.getAddress({ accountNumber: bip44Params.accountNumber, wallet, }) @@ -471,14 +471,18 @@ export class IdleOpportunity if (wallet.supportsOfflineSigning()) { const signedTx = await chainAdapter.signTransaction({ txToSign, wallet }) if (this.#internals.dryRun) return signedTx - return chainAdapter.broadcastTransaction({ from, to: tx.to, hex: signedTx }) + return chainAdapter.broadcastTransaction({ + senderAddress, + receiverAddress: undefined, // no receiver for this contract call + hex: signedTx, + }) } else if (wallet.supportsBroadcast() && chainAdapter.signAndBroadcastTransaction) { if (this.#internals.dryRun) { throw new Error(`Cannot perform a dry run with wallet of type ${wallet.getVendor()}`) } return chainAdapter.signAndBroadcastTransaction({ - from, - to: tx.to, + senderAddress, + receiverAddress: undefined, // no receiver for this contract call signTxInput: { txToSign, wallet }, }) } else { diff --git a/src/lib/investor/investor-yearn/YearnOpportunity.ts b/src/lib/investor/investor-yearn/YearnOpportunity.ts index 2c81d3996e4..29a185251d6 100644 --- a/src/lib/investor/investor-yearn/YearnOpportunity.ts +++ b/src/lib/investor/investor-yearn/YearnOpportunity.ts @@ -195,7 +195,7 @@ export class YearnOpportunity addressNList: toAddressNList(bip44Params), } - const from = await chainAdapter.getAddress({ + const senderAddress = await chainAdapter.getAddress({ accountNumber: bip44Params.accountNumber, wallet, }) @@ -203,14 +203,18 @@ export class YearnOpportunity if (wallet.supportsOfflineSigning()) { const signedTx = await chainAdapter.signTransaction({ txToSign, wallet }) if (this.#internals.dryRun) return signedTx - return chainAdapter.broadcastTransaction({ from, to: tx.to, hex: signedTx }) + return chainAdapter.broadcastTransaction({ + senderAddress, + receiverAddress: undefined, // no receiver for this contract call + hex: signedTx, + }) } else if (wallet.supportsBroadcast() && chainAdapter.signAndBroadcastTransaction) { if (this.#internals.dryRun) { throw new Error(`Cannot perform a dry run with wallet of type ${wallet.getVendor()}`) } return chainAdapter.signAndBroadcastTransaction({ - from, - to: tx.to, + senderAddress, + receiverAddress: undefined, // no receiver for this contract call signTxInput: { txToSign, wallet }, }) } else { diff --git a/src/lib/swapper/types.ts b/src/lib/swapper/types.ts index 3c30509f94e..3b2928bd209 100644 --- a/src/lib/swapper/types.ts +++ b/src/lib/swapper/types.ts @@ -215,7 +215,8 @@ export type GetUnsignedCosmosSdkTransactionArgs = CommonGetUnsignedTransactionAr export type UnsignedTx = Nominal, 'UnsignedTx'> export type ExecuteTradeArgs = { - accountNumber: number + senderAddress: string + receiverAddress: string txToSign: UnsignedTx wallet: HDWallet chainId: ChainId diff --git a/src/lib/utils/evm.ts b/src/lib/utils/evm.ts index 105477b8101..1f91f2a80fe 100644 --- a/src/lib/utils/evm.ts +++ b/src/lib/utils/evm.ts @@ -34,14 +34,15 @@ type BuildArgs = { } type BroadcastArgs = { - accountNumber: number + senderAddress: string + receiverAddress: string | undefined adapter: EvmChainAdapter txToSign: SignTx wallet: HDWallet } type BuildAndBroadcastArgs = BuildArgs & - Omit + Omit type CreateBuildCustomTxInputArgs = { accountNumber: number @@ -169,13 +170,19 @@ export const createBuildCustomApiTxInput = async ( return { ...args, ...fees } } -export const buildAndBroadcast = async ({ adapter, buildCustomTxInput }: BuildAndBroadcastArgs) => { +export const buildAndBroadcast = async ({ + adapter, + buildCustomTxInput, + receiverAddress, +}: BuildAndBroadcastArgs) => { + const senderAddress = await adapter.getAddress(buildCustomTxInput) const { txToSign } = await adapter.buildCustomTx(buildCustomTxInput) return signAndBroadcast({ adapter, txToSign, wallet: buildCustomTxInput.wallet, - accountNumber: buildCustomTxInput.accountNumber, + senderAddress, + receiverAddress, }) } @@ -183,22 +190,27 @@ export const signAndBroadcast = async ({ adapter, txToSign, wallet, - accountNumber, + senderAddress, + receiverAddress, }: BroadcastArgs) => { if (!wallet) throw new Error('Wallet is required to broadcast EVM Txs') - const from = await adapter.getAddress({ accountNumber, wallet }) - if (wallet.supportsOfflineSigning()) { const signedTx = await adapter.signTransaction({ txToSign, wallet }) - const txid = await adapter.broadcastTransaction({ from, to: txToSign.to, hex: signedTx }) + const txid = await adapter.broadcastTransaction({ + senderAddress, + receiverAddress, + hex: signedTx, + }) return txid } if (wallet.supportsBroadcast() && adapter.signAndBroadcastTransaction) { + // note that txToSign.to is often a contract address and not the actual receiver + // TODO: do we want to validate contract addresses too? const txid = await adapter.signAndBroadcastTransaction({ - from, - to: txToSign.to, + senderAddress, + receiverAddress, signTxInput: { txToSign, wallet }, }) return txid @@ -243,9 +255,21 @@ export const assertGetEvmChainAdapter = (chainId: ChainId | KnownChainIds): EvmC return adapter } -export const executeEvmTrade = ({ txToSign, wallet, chainId, accountNumber }: ExecuteTradeArgs) => { +export const executeEvmTrade = ({ + txToSign, + wallet, + chainId, + senderAddress, + receiverAddress, +}: ExecuteTradeArgs) => { const adapter = assertGetEvmChainAdapter(chainId) - return signAndBroadcast({ adapter, wallet, txToSign: txToSign as ETHSignTx, accountNumber }) + return signAndBroadcast({ + adapter, + wallet, + txToSign: txToSign as ETHSignTx, + senderAddress, + receiverAddress, + }) } export const executeEvmTransaction = ( diff --git a/src/plugins/cosmos/hooks/useStakingAction/useStakingAction.tsx b/src/plugins/cosmos/hooks/useStakingAction/useStakingAction.tsx index 2f3982e3d45..9060631f18a 100644 --- a/src/plugins/cosmos/hooks/useStakingAction/useStakingAction.tsx +++ b/src/plugins/cosmos/hooks/useStakingAction/useStakingAction.tsx @@ -93,10 +93,10 @@ export const useStakingAction = () => { throw new Error(`unsupported staking action: ${action}`) } })() - const from = await adapter.getAddress({ accountNumber, wallet }) + const senderAddress = await adapter.getAddress({ accountNumber, wallet }) return adapter.signAndBroadcastTransaction({ - from, - to: validator, + senderAddress, + receiverAddress: undefined, // no receiver for this contract call signTxInput: { txToSign, wallet }, }) } catch (error) { diff --git a/src/plugins/walletConnectToDapps/utils/EIP155RequestHandlerUtil.ts b/src/plugins/walletConnectToDapps/utils/EIP155RequestHandlerUtil.ts index 9d0bed984e4..18a41d21a8e 100644 --- a/src/plugins/walletConnectToDapps/utils/EIP155RequestHandlerUtil.ts +++ b/src/plugins/walletConnectToDapps/utils/EIP155RequestHandlerUtil.ts @@ -88,7 +88,7 @@ export const approveEIP155Request = async ({ const didUserChangeNonce = maybeAdvancedParamsNonce && maybeAdvancedParamsNonce !== sendTransaction.nonce const fees = await getFeesForTx(sendTransaction, chainAdapter, accountId) - const from = await chainAdapter.getAddress({ accountNumber, wallet }) + const senderAddress = await chainAdapter.getAddress({ accountNumber, wallet }) const gasData = getGasData(customTransactionData, fees) const { txToSign: txToSignWithPossibleWrongNonce } = await chainAdapter.buildCustomTx({ wallet, @@ -109,8 +109,8 @@ export const approveEIP155Request = async ({ wallet, }) const txHash = await chainAdapter.broadcastTransaction({ - from, - to: txToSign.to, + senderAddress, + receiverAddress: txToSign.to, hex: signedTx, }) return formatJsonRpcResult(id, txHash) From 0c9e8706904a67f8c0b3a2c7d0f5265aabb740cf Mon Sep 17 00:00:00 2001 From: woodenfurniture <125113430+woodenfurniture@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:41:47 +1100 Subject: [PATCH 04/12] fix: tests --- .../src/evm/arbitrum/ArbitrumChainAdapter.test.ts | 4 ++-- .../src/evm/avalanche/AvalancheChainAdapter.test.ts | 4 ++-- .../src/evm/bnbsmartchain/BscChainAdapter.test.ts | 4 ++-- .../src/evm/ethereum/EthereumChainAdapter.test.ts | 4 ++-- .../chain-adapters/src/evm/gnosis/GnosisChainAdapter.test.ts | 4 ++-- .../src/evm/polygon/PolygonChainAdapter.test.ts | 4 ++-- .../src/utxo/bitcoin/BitcoinChainAdapter.test.ts | 4 ++-- .../src/utxo/bitcoincash/BitcoinCashChainAdapter.test.ts | 4 ++-- .../src/utxo/dogecoin/DogecoinChainAdapter.test.ts | 4 ++-- .../src/utxo/litecoin/LitecoinChainAdapter.test.ts | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/chain-adapters/src/evm/arbitrum/ArbitrumChainAdapter.test.ts b/packages/chain-adapters/src/evm/arbitrum/ArbitrumChainAdapter.test.ts index c59359bf88d..67be52666fb 100644 --- a/packages/chain-adapters/src/evm/arbitrum/ArbitrumChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/arbitrum/ArbitrumChainAdapter.test.ts @@ -21,8 +21,8 @@ import { bn } from '../../utils/bignumber' import type { ChainAdapterArgs, EvmChainId } from '../EvmBaseAdapter' import * as arbitrum from './ArbitrumChainAdapter' -jest.mock('../utils/validatePubkey', () => ({ - validatePubkey: jest.fn(), +jest.mock('../../utils/validateAddress', () => ({ + validateAddress: jest.fn(), })) const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' diff --git a/packages/chain-adapters/src/evm/avalanche/AvalancheChainAdapter.test.ts b/packages/chain-adapters/src/evm/avalanche/AvalancheChainAdapter.test.ts index 18756ce9295..fa2e312d467 100644 --- a/packages/chain-adapters/src/evm/avalanche/AvalancheChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/avalanche/AvalancheChainAdapter.test.ts @@ -26,8 +26,8 @@ import { bn } from '../../utils/bignumber' import type { ChainAdapterArgs, EvmChainId } from '../EvmBaseAdapter' import * as avalanche from './AvalancheChainAdapter' -jest.mock('../utils/validatePubkey', () => ({ - validatePubkey: jest.fn(), +jest.mock('../../utils/validateAddress', () => ({ + validateAddress: jest.fn(), })) const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' diff --git a/packages/chain-adapters/src/evm/bnbsmartchain/BscChainAdapter.test.ts b/packages/chain-adapters/src/evm/bnbsmartchain/BscChainAdapter.test.ts index 7a1bf5dd96f..8fe5f3b9fd7 100644 --- a/packages/chain-adapters/src/evm/bnbsmartchain/BscChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/bnbsmartchain/BscChainAdapter.test.ts @@ -21,8 +21,8 @@ import { bn } from '../../utils/bignumber' import type { ChainAdapterArgs, EvmChainId } from '../EvmBaseAdapter' import * as bsc from './BscChainAdapter' -jest.mock('../utils/validatePubkey', () => ({ - validatePubkey: jest.fn(), +jest.mock('../../utils/validateAddress', () => ({ + validateAddress: jest.fn(), })) const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' diff --git a/packages/chain-adapters/src/evm/ethereum/EthereumChainAdapter.test.ts b/packages/chain-adapters/src/evm/ethereum/EthereumChainAdapter.test.ts index 92b0cb06b69..b236a3fbd35 100644 --- a/packages/chain-adapters/src/evm/ethereum/EthereumChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/ethereum/EthereumChainAdapter.test.ts @@ -21,8 +21,8 @@ import { bn } from '../../utils/bignumber' import type { ChainAdapterArgs, EvmChainId } from '../EvmBaseAdapter' import * as ethereum from './EthereumChainAdapter' -jest.mock('../utils/validatePubkey', () => ({ - validatePubkey: jest.fn(), +jest.mock('../../utils/validateAddress', () => ({ + validateAddress: jest.fn(), })) const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' diff --git a/packages/chain-adapters/src/evm/gnosis/GnosisChainAdapter.test.ts b/packages/chain-adapters/src/evm/gnosis/GnosisChainAdapter.test.ts index 734ab3edc2e..7e3ccb86563 100644 --- a/packages/chain-adapters/src/evm/gnosis/GnosisChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/gnosis/GnosisChainAdapter.test.ts @@ -22,8 +22,8 @@ import { bn } from '../../utils/bignumber' import type { ChainAdapterArgs, EvmChainId } from '../EvmBaseAdapter' import * as gnosis from './GnosisChainAdapter' -jest.mock('../utils/validatePubkey', () => ({ - validatePubkey: jest.fn(), +jest.mock('../../utils/validateAddress', () => ({ + validateAddress: jest.fn(), })) const EOA_ADDRESS = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' diff --git a/packages/chain-adapters/src/evm/polygon/PolygonChainAdapter.test.ts b/packages/chain-adapters/src/evm/polygon/PolygonChainAdapter.test.ts index 1e69e3853b3..2f1e1926d84 100644 --- a/packages/chain-adapters/src/evm/polygon/PolygonChainAdapter.test.ts +++ b/packages/chain-adapters/src/evm/polygon/PolygonChainAdapter.test.ts @@ -21,8 +21,8 @@ import { bn } from '../../utils/bignumber' import type { ChainAdapterArgs, EvmChainId } from '../EvmBaseAdapter' import * as polygon from './PolygonChainAdapter' -jest.mock('../utils/validatePubkey', () => ({ - validatePubkey: jest.fn(), +jest.mock('../../utils/validateAddress', () => ({ + validateAddress: jest.fn(), })) const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' diff --git a/packages/chain-adapters/src/utxo/bitcoin/BitcoinChainAdapter.test.ts b/packages/chain-adapters/src/utxo/bitcoin/BitcoinChainAdapter.test.ts index 62cfdac8700..8379bec282c 100644 --- a/packages/chain-adapters/src/utxo/bitcoin/BitcoinChainAdapter.test.ts +++ b/packages/chain-adapters/src/utxo/bitcoin/BitcoinChainAdapter.test.ts @@ -16,8 +16,8 @@ import type { Account, BuildSendTxInput, GetFeeDataInput } from '../../types' import type { ChainAdapterArgs, UtxoChainId } from '../UtxoBaseAdapter' import * as bitcoin from './BitcoinChainAdapter' -jest.mock('../utils/validatePubkey', () => ({ - validatePubkey: jest.fn(), +jest.mock('../../utils/validateAddress', () => ({ + validateAddress: jest.fn(), })) const testMnemonic = 'alcohol woman abuse must during monitor noble actual mixed trade anger aisle' diff --git a/packages/chain-adapters/src/utxo/bitcoincash/BitcoinCashChainAdapter.test.ts b/packages/chain-adapters/src/utxo/bitcoincash/BitcoinCashChainAdapter.test.ts index b99efbe0b33..5873a32507b 100644 --- a/packages/chain-adapters/src/utxo/bitcoincash/BitcoinCashChainAdapter.test.ts +++ b/packages/chain-adapters/src/utxo/bitcoincash/BitcoinCashChainAdapter.test.ts @@ -22,8 +22,8 @@ import type { Account, BuildSendTxInput, GetFeeDataInput } from '../../types' import type { ChainAdapterArgs, UtxoChainId } from '../UtxoBaseAdapter' import * as bitcoincash from './BitcoinCashChainAdapter' -jest.mock('../utils/validatePubkey', () => ({ - validatePubkey: jest.fn(), +jest.mock('../../utils/validateAddress', () => ({ + validateAddress: jest.fn(), })) const testMnemonic = 'alcohol woman abuse must during monitor noble actual mixed trade anger aisle' diff --git a/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.test.ts b/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.test.ts index 7e17314758a..b8348633699 100644 --- a/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.test.ts +++ b/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.test.ts @@ -16,8 +16,8 @@ import type { Account, BuildSendTxInput, GetFeeDataInput } from '../../types' import type { ChainAdapterArgs, UtxoChainId } from '../UtxoBaseAdapter' import * as dogecoin from './DogecoinChainAdapter' -jest.mock('../utils/validatePubkey', () => ({ - validatePubkey: jest.fn(), +jest.mock('../../utils/validateAddress', () => ({ + validateAddress: jest.fn(), })) const testMnemonic = 'alcohol woman abuse must during monitor noble actual mixed trade anger aisle' diff --git a/packages/chain-adapters/src/utxo/litecoin/LitecoinChainAdapter.test.ts b/packages/chain-adapters/src/utxo/litecoin/LitecoinChainAdapter.test.ts index 1907e39a0db..e804908e050 100644 --- a/packages/chain-adapters/src/utxo/litecoin/LitecoinChainAdapter.test.ts +++ b/packages/chain-adapters/src/utxo/litecoin/LitecoinChainAdapter.test.ts @@ -17,8 +17,8 @@ import type { Account, BuildSendTxInput, GetFeeDataInput } from '../../types' import type { ChainAdapterArgs, UtxoChainId } from '../UtxoBaseAdapter' import * as litecoin from './LitecoinChainAdapter' -jest.mock('../utils/validatePubkey', () => ({ - validatePubkey: jest.fn(), +jest.mock('../../utils/validateAddress', () => ({ + validateAddress: jest.fn(), })) const testMnemonic = 'alcohol woman abuse must during monitor noble actual mixed trade anger aisle' From c9618317be99d886946091aac4b2bfd021e4baf9 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Tue, 10 Oct 2023 10:52:50 +0200 Subject: [PATCH 05/12] fix: handleSend senderAddress as from ?? account --- src/components/Modals/Send/utils.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/Modals/Send/utils.ts b/src/components/Modals/Send/utils.ts index 6b3b2bf4d2e..b569844ecf6 100644 --- a/src/components/Modals/Send/utils.ts +++ b/src/components/Modals/Send/utils.ts @@ -129,7 +129,7 @@ export const handleSend = async ({ const chainId = adapter.getChainId() - const { estimatedFees, feeType, to, memo, from } = sendInput + const { estimatedFees, feeType, to, memo, from, accountId } = sendInput if (!accountMetadata) throw new Error(`useFormSend: no accountMetadata for ${sendInput.accountId}`) @@ -215,6 +215,8 @@ export const handleSend = async ({ const txToSign = result.txToSign + const { account } = fromAccountId(accountId) + const broadcastTXID = await (async () => { if (wallet.supportsOfflineSigning()) { const signedTx = await adapter.signTransaction({ @@ -222,7 +224,7 @@ export const handleSend = async ({ wallet, }) return adapter.broadcastTransaction({ - senderAddress: from, + senderAddress: from ?? account, receiverAddress: to, hex: signedTx, }) @@ -235,7 +237,7 @@ export const handleSend = async ({ throw new Error('signAndBroadcastTransaction undefined for wallet') } return adapter.signAndBroadcastTransaction({ - senderAddress: from, + senderAddress: from ?? account, receiverAddress: to, signTxInput: { txToSign, wallet }, }) From ba620b990669e390da3d1e5e20329c0d26e468c1 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Tue, 10 Oct 2023 14:18:12 +0200 Subject: [PATCH 06/12] fix: wtf was that --- .../Modals/Send/hooks/useFormSend/useFormSend.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Modals/Send/hooks/useFormSend/useFormSend.test.tsx b/src/components/Modals/Send/hooks/useFormSend/useFormSend.test.tsx index 8067833b3aa..c630d359b52 100644 --- a/src/components/Modals/Send/hooks/useFormSend/useFormSend.test.tsx +++ b/src/components/Modals/Send/hooks/useFormSend/useFormSend.test.tsx @@ -110,7 +110,7 @@ const formData: SendInput = { [SendFormFields.FiatAmount]: '3500', [SendFormFields.FiatSymbol]: 'USD', [SendFormFields.SendMax]: false, - [SendFormFields.AccountId]: 'eip155:1/erc20:0x3155ba85d5f96b2d030a4966af206230e46849cb', + [SendFormFields.AccountId]: 'eip155:1:0x3155ba85d5f96b2d030a4966af206230e46849cb', } const formDataEnsAddress = { ...formData, [SendFormFields.To]: 'willywonka.eth' } From 9060a2d5360e369211138f976396285f16675558 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Tue, 10 Oct 2023 14:39:13 +0200 Subject: [PATCH 07/12] fix: utxo don't expose xpub to trmlabs --- src/components/Modals/Send/utils.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/Modals/Send/utils.ts b/src/components/Modals/Send/utils.ts index b569844ecf6..89011b860d4 100644 --- a/src/components/Modals/Send/utils.ts +++ b/src/components/Modals/Send/utils.ts @@ -215,7 +215,10 @@ export const handleSend = async ({ const txToSign = result.txToSign - const { account } = fromAccountId(accountId) + const senderAddress = await adapter.getAddress({ + accountNumber: accountMetadata.bip44Params.accountNumber, + wallet, + }) const broadcastTXID = await (async () => { if (wallet.supportsOfflineSigning()) { @@ -224,7 +227,7 @@ export const handleSend = async ({ wallet, }) return adapter.broadcastTransaction({ - senderAddress: from ?? account, + senderAddress, receiverAddress: to, hex: signedTx, }) @@ -237,7 +240,7 @@ export const handleSend = async ({ throw new Error('signAndBroadcastTransaction undefined for wallet') } return adapter.signAndBroadcastTransaction({ - senderAddress: from ?? account, + senderAddress, receiverAddress: to, signTxInput: { txToSign, wallet }, }) From c1425571341a01412c35f6c7aa0638414d8b7c9d Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:12:43 +0200 Subject: [PATCH 08/12] fix: useTradeExecution senderAddress --- .../MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts b/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts index b5887d0d9b9..9e4dd4ecc5c 100644 --- a/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts +++ b/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts @@ -167,6 +167,7 @@ export const useTradeExecution = ({ if (accountType === undefined) throw Error('Missing UTXO account type') const adapter = assertGetUtxoChainAdapter(chainId) const { xpub } = await adapter.getPublicKey(wallet, accountNumber, accountType) + const senderAddress = await adapter.getAddress({ accountNumber, wallet }) const output = await execution.execUtxoTransaction({ swapperName, tradeQuote, @@ -180,7 +181,7 @@ export const useTradeExecution = ({ wallet, }) return adapter.broadcastTransaction({ - senderAddress: xpub, + senderAddress, receiverAddress: tradeQuote.receiveAddress, hex: signedTx, }) From a857b60a9a530b82b8560ea5bb8c94d64cc282b8 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:20:34 +0200 Subject: [PATCH 09/12] fix: receiverAddress as address without bitcoinCash prefix --- .../useTradeExecution/useTradeExecution.ts | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts b/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts index 9e4dd4ecc5c..986b79acf46 100644 --- a/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts +++ b/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts @@ -1,5 +1,5 @@ import type { StdSignDoc } from '@keplr-wallet/types' -import { CHAIN_NAMESPACE, fromChainId } from '@shapeshiftoss/caip' +import { bchAssetId, CHAIN_NAMESPACE, fromChainId } from '@shapeshiftoss/caip' import type { SignMessageInput } from '@shapeshiftoss/chain-adapters' import { toAddressNList } from '@shapeshiftoss/chain-adapters' import type { BuildCustomTxInput } from '@shapeshiftoss/chain-adapters/src/evm/types' @@ -103,10 +103,11 @@ export const useTradeExecution = ({ const { accountType, bip44Params } = accountMetadata const accountNumber = bip44Params.accountNumber - const chainId = tradeQuote.steps[activeStepOrDefault].sellAsset.chainId + const stepSellAssetChainId = tradeQuote.steps[activeStepOrDefault].sellAsset.chainId + const stepBuyAssetAssetId = tradeQuote.steps[activeStepOrDefault].buyAsset.assetId if (swapperName === SwapperName.CowSwap) { - const adapter = assertGetEvmChainAdapter(chainId) + const adapter = assertGetEvmChainAdapter(stepSellAssetChainId) const from = await adapter.getAddress({ accountNumber, wallet }) const output = await execution.execEvmMessage({ swapperName, @@ -133,12 +134,18 @@ export const useTradeExecution = ({ return } - const { chainNamespace } = fromChainId(chainId) + const { chainNamespace: stepSellAssetChainNamespace } = fromChainId(stepSellAssetChainId) - switch (chainNamespace) { + const receiverAddress = + tradeQuote.receiveAddress && stepBuyAssetAssetId === bchAssetId + ? tradeQuote.receiveAddress.replace('bitcoincash:', '') + : tradeQuote.receiveAddress + + switch (stepSellAssetChainNamespace) { case CHAIN_NAMESPACE.Evm: { - const adapter = assertGetEvmChainAdapter(chainId) + const adapter = assertGetEvmChainAdapter(stepSellAssetChainId) const from = await adapter.getAddress({ accountNumber, wallet }) + const output = await execution.execEvmTransaction({ swapperName, tradeQuote, @@ -156,7 +163,7 @@ export const useTradeExecution = ({ txToSign, wallet, senderAddress: from, - receiverAddress: tradeQuote.receiveAddress, + receiverAddress, }) }, }) @@ -165,7 +172,7 @@ export const useTradeExecution = ({ } case CHAIN_NAMESPACE.Utxo: { if (accountType === undefined) throw Error('Missing UTXO account type') - const adapter = assertGetUtxoChainAdapter(chainId) + const adapter = assertGetUtxoChainAdapter(stepSellAssetChainId) const { xpub } = await adapter.getPublicKey(wallet, accountNumber, accountType) const senderAddress = await adapter.getAddress({ accountNumber, wallet }) const output = await execution.execUtxoTransaction({ @@ -182,7 +189,7 @@ export const useTradeExecution = ({ }) return adapter.broadcastTransaction({ senderAddress, - receiverAddress: tradeQuote.receiveAddress, + receiverAddress, hex: signedTx, }) }, @@ -191,7 +198,7 @@ export const useTradeExecution = ({ return } case CHAIN_NAMESPACE.CosmosSdk: { - const adapter = assertGetCosmosSdkChainAdapter(chainId) + const adapter = assertGetCosmosSdkChainAdapter(stepSellAssetChainId) const from = await adapter.getAddress({ accountNumber, wallet }) const output = await execution.execCosmosSdkTransaction({ swapperName, @@ -230,7 +237,7 @@ export const useTradeExecution = ({ return } default: - assertUnreachable(chainNamespace) + assertUnreachable(stepSellAssetChainNamespace) } }) }, [ @@ -240,8 +247,8 @@ export const useTradeExecution = ({ swapperName, sellAssetAccountId, activeStepOrDefault, - slippageTolerancePercentageDecimal, dispatch, + slippageTolerancePercentageDecimal, ]) useEffect(() => { From d0fe358d3fbd7bce6a4717fd55e272dc9dc01750 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:28:58 +0200 Subject: [PATCH 10/12] fix: senderAddress as address without bitcoinCash prefix --- .../hooks/useTradeExecution/useTradeExecution.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts b/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts index 986b79acf46..f793c0b20f5 100644 --- a/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts +++ b/src/components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution.ts @@ -104,6 +104,7 @@ export const useTradeExecution = ({ const { accountType, bip44Params } = accountMetadata const accountNumber = bip44Params.accountNumber const stepSellAssetChainId = tradeQuote.steps[activeStepOrDefault].sellAsset.chainId + const stepSellAssetAssetId = tradeQuote.steps[activeStepOrDefault].sellAsset.assetId const stepBuyAssetAssetId = tradeQuote.steps[activeStepOrDefault].buyAsset.assetId if (swapperName === SwapperName.CowSwap) { @@ -174,7 +175,12 @@ export const useTradeExecution = ({ if (accountType === undefined) throw Error('Missing UTXO account type') const adapter = assertGetUtxoChainAdapter(stepSellAssetChainId) const { xpub } = await adapter.getPublicKey(wallet, accountNumber, accountType) - const senderAddress = await adapter.getAddress({ accountNumber, wallet }) + const _senderAddress = await adapter.getAddress({ accountNumber, wallet }) + const senderAddress = + stepSellAssetAssetId === bchAssetId + ? _senderAddress.replace('bitcoincash:', '') + : _senderAddress + const output = await execution.execUtxoTransaction({ swapperName, tradeQuote, From 57df39978591072076daabf7a505e631b9fb9c6c Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:47:23 +0200 Subject: [PATCH 11/12] fix: lint --- src/components/Modals/Send/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Modals/Send/utils.ts b/src/components/Modals/Send/utils.ts index 89011b860d4..0d3ac715378 100644 --- a/src/components/Modals/Send/utils.ts +++ b/src/components/Modals/Send/utils.ts @@ -129,7 +129,7 @@ export const handleSend = async ({ const chainId = adapter.getChainId() - const { estimatedFees, feeType, to, memo, from, accountId } = sendInput + const { estimatedFees, feeType, to, memo, from } = sendInput if (!accountMetadata) throw new Error(`useFormSend: no accountMetadata for ${sendInput.accountId}`) From d8a5e37bf4ac6735a07d5a2ab378fdf9486bc75a Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:53:08 +0200 Subject: [PATCH 12/12] fix: mock adapter.getAddress in tests --- .../Send/hooks/useFormSend/useFormSend.test.tsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/components/Modals/Send/hooks/useFormSend/useFormSend.test.tsx b/src/components/Modals/Send/hooks/useFormSend/useFormSend.test.tsx index c630d359b52..e25e0cf5f7f 100644 --- a/src/components/Modals/Send/hooks/useFormSend/useFormSend.test.tsx +++ b/src/components/Modals/Send/hooks/useFormSend/useFormSend.test.tsx @@ -115,7 +115,9 @@ const formData: SendInput = { const formDataEnsAddress = { ...formData, [SendFormFields.To]: 'willywonka.eth' } -const textTxToSign = { +const testSendAddress = '0x3155ba85d5f96b2d030a4966af206230e46849cb' + +const testTxToSign = { addressNList: [2147483692, 2147483708, 2147483648, 0, 0], value: '0x0', to: '0x3155ba85d5f96b2d030a4966af206230e46849cb', @@ -165,7 +167,8 @@ describe.each([ } }) const mockAdapter = { - buildSendTransaction: () => Promise.resolve(textTxToSign), + getAddress: () => Promise.resolve(testSendAddress), + buildSendTransaction: () => Promise.resolve(testTxToSign), signTransaction: () => Promise.resolve(testSignedTx), broadcastTransaction: () => Promise.resolve(expectedTx), } @@ -229,7 +232,8 @@ describe.each([ } }) const mockAdapter = { - buildSendTransaction: () => Promise.resolve(textTxToSign), + getAddress: () => Promise.resolve(testSendAddress), + buildSendTransaction: () => Promise.resolve(testTxToSign), signTransaction: () => Promise.resolve(testSignedTx), broadcastTransaction: () => Promise.resolve(expectedTx), } @@ -290,7 +294,8 @@ describe.each([ } }) const mockAdapter = { - buildSendTransaction: () => Promise.resolve(textTxToSign), + getAddress: () => Promise.resolve(testSendAddress), + buildSendTransaction: () => Promise.resolve(testTxToSign), signAndBroadcastTransaction, } @@ -356,7 +361,8 @@ describe.each([ } }) const mockAdapter = { - buildSendTransaction: () => Promise.resolve(textTxToSign), + getAddress: () => Promise.resolve(testSendAddress), + buildSendTransaction: () => Promise.resolve(testTxToSign), signAndBroadcastTransaction, } @@ -410,6 +416,7 @@ describe.each([ }) const mockAdapter = { + getAddress: () => Promise.resolve(testSendAddress), buildSendTransaction: () => Promise.reject('All these calls failed'), signTransaction: () => Promise.reject('All these calls failed'), broadcastTransaction: () => Promise.reject('All these calls failed'),