-
Notifications
You must be signed in to change notification settings - Fork 232
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
37d695d
commit 8607454
Showing
7 changed files
with
457 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { type Address, encodeFunctionData, erc20Abi } from 'viem'; | ||
import { type Mock, describe, expect, it, vi } from 'vitest'; | ||
import { buildSendTransaction } from './buildSendTransaction'; | ||
|
||
vi.mock('viem', async () => { | ||
const actual = await vi.importActual('viem'); | ||
return { | ||
...actual, | ||
encodeFunctionData: vi.fn(), | ||
}; | ||
}); | ||
|
||
describe('buildSendTransaction', () => { | ||
const mockRecipient = '0x742d35Cc6634C0532925a3b844Bc454e4438f44e'; | ||
const mockToken = '0x1234567890123456789012345678901234567890'; | ||
const mockAmount = 1000000000000000000n; // 1 ETH/token in wei | ||
|
||
it('should build native ETH transfer transaction', () => { | ||
const result = buildSendTransaction({ | ||
recipientAddress: mockRecipient, | ||
tokenAddress: undefined as unknown as Address, // type assertion okay because we're testing the case where tokenAddress is undefined | ||
amount: mockAmount, | ||
}); | ||
|
||
expect(result).toEqual({ | ||
to: mockRecipient, | ||
data: '0x', | ||
value: mockAmount, | ||
}); | ||
}); | ||
|
||
it('should build ERC20 token transfer transaction', () => { | ||
const expectedCallData = encodeFunctionData({ | ||
abi: erc20Abi, | ||
functionName: 'transfer', | ||
args: [mockRecipient, mockAmount], | ||
}); | ||
|
||
const result = buildSendTransaction({ | ||
recipientAddress: mockRecipient, | ||
tokenAddress: mockToken, | ||
amount: mockAmount, | ||
}); | ||
|
||
expect(result).toEqual({ | ||
to: mockToken, | ||
data: expectedCallData, | ||
}); | ||
}); | ||
|
||
it('should handle Error objects', () => { | ||
(encodeFunctionData as Mock).mockImplementation(() => { | ||
throw new Error('Test error'); | ||
}); | ||
const result = buildSendTransaction({ | ||
recipientAddress: mockRecipient, | ||
tokenAddress: mockToken, | ||
amount: mockAmount, | ||
}); | ||
|
||
expect(result).toMatchObject({ | ||
code: 'AmBSeTa01', | ||
message: 'Could not build transfer transaction', | ||
error: 'Test error', | ||
}); | ||
}); | ||
|
||
it('should handle non-Error objects', () => { | ||
(encodeFunctionData as Mock).mockImplementation(() => { | ||
throw 'Some string error'; | ||
}); | ||
const result = buildSendTransaction({ | ||
recipientAddress: mockRecipient, | ||
tokenAddress: mockToken, | ||
amount: mockAmount, | ||
}); | ||
|
||
expect(result).toMatchObject({ | ||
code: 'AmBSeTa01', | ||
message: 'Could not build transfer transaction', | ||
error: 'Some string error', | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { encodeFunctionData, erc20Abi } from 'viem'; | ||
import type { | ||
BuildSendTransactionParams, | ||
BuildSendTransactionResponse, | ||
} from './types'; | ||
|
||
export function buildSendTransaction({ | ||
recipientAddress, | ||
tokenAddress, | ||
amount, | ||
}: BuildSendTransactionParams): BuildSendTransactionResponse { | ||
// if no token address, we are sending native ETH | ||
// and the data prop is empty | ||
if (!tokenAddress) { | ||
return { | ||
to: recipientAddress, | ||
data: '0x', | ||
value: amount, | ||
}; | ||
} | ||
|
||
try { | ||
const transferCallData = encodeFunctionData({ | ||
abi: erc20Abi, | ||
functionName: 'transfer', | ||
args: [recipientAddress, amount], | ||
}); | ||
return { | ||
to: tokenAddress, | ||
data: transferCallData, | ||
}; | ||
} catch (error) { | ||
const message = error instanceof Error ? error.message : `${error}`; | ||
return { | ||
code: 'AmBSeTa01', // Api Module Build Send Transaction Error 01 | ||
error: message, | ||
message: 'Could not build transfer transaction', | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import { buildSendTransaction } from '@/api/buildSendTransaction'; | ||
import type { Token } from '@/token'; | ||
import { renderHook } from '@testing-library/react'; | ||
import { type Address, parseUnits } from 'viem'; | ||
import { type Mock, beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { useSendTransaction } from './useSendTransaction'; | ||
|
||
vi.mock('@/api/buildSendTransaction', () => ({ | ||
buildSendTransaction: vi.fn(), | ||
})); | ||
|
||
describe('useSendTransaction', () => { | ||
const mockToken: Token = { | ||
symbol: 'TOKEN', | ||
decimals: 18, | ||
address: '0x0987654321098765432109876543210987654321', | ||
chainId: 8453, | ||
image: '', | ||
name: '', | ||
}; | ||
const mockRecipientAddress = '0x1234567890123456789012345678901234567890'; | ||
const mockCallData = { | ||
to: mockRecipientAddress, | ||
data: mockToken.address, | ||
value: parseUnits('1.0', 18), | ||
}; | ||
|
||
beforeEach(() => { | ||
vi.resetAllMocks(); | ||
}); | ||
|
||
it('returns empty calls when token is null', () => { | ||
const { result } = renderHook(() => | ||
useSendTransaction({ | ||
recipientAddress: mockRecipientAddress, | ||
token: null, | ||
amount: '1.0', | ||
}), | ||
); | ||
|
||
expect(result.current).toEqual({ | ||
code: 'AmBSeTx01', // Api Module Build Send Transaction Error 01 | ||
error: 'No token provided', | ||
message: 'Could not build send transaction', | ||
}); | ||
}); | ||
|
||
it('handles ETH transfers correctly', () => { | ||
(buildSendTransaction as Mock).mockReturnValue({ | ||
...mockCallData, | ||
data: '', | ||
}); | ||
const { result } = renderHook(() => | ||
useSendTransaction({ | ||
recipientAddress: mockRecipientAddress, | ||
token: { ...mockToken, address: '', symbol: 'ETH' }, | ||
amount: '1.0', | ||
}), | ||
); | ||
|
||
expect(buildSendTransaction).toHaveBeenCalledWith({ | ||
recipientAddress: mockRecipientAddress, | ||
tokenAddress: null, | ||
amount: parseUnits('1.0', 18), | ||
}); | ||
expect(result.current).toEqual({ | ||
to: mockRecipientAddress, | ||
data: '', | ||
value: parseUnits('1.0', 18), | ||
}); | ||
}); | ||
|
||
it('returns error for non-ETH token without address', () => { | ||
const { result } = renderHook(() => | ||
useSendTransaction({ | ||
recipientAddress: mockRecipientAddress, | ||
token: { | ||
...mockToken, | ||
symbol: 'INVALID', | ||
address: undefined as unknown as Address, // type assertion okay because we're testing the case where address is undefined | ||
}, | ||
amount: '1.0', | ||
}), | ||
); | ||
|
||
expect(result.current).toEqual({ | ||
code: 'AmBSeTx02', // Api Module Build Send Transaction Error 02 | ||
error: 'No token address provided for non-ETH token', | ||
message: 'Could not build send transaction', | ||
}); | ||
}); | ||
|
||
it('handles ERC20 token transfers correctly', () => { | ||
const mockDecimals = 6; | ||
const expectedCallData = { | ||
to: mockRecipientAddress, | ||
data: mockToken.address, | ||
value: parseUnits('100', mockDecimals), | ||
}; | ||
(buildSendTransaction as Mock).mockReturnValue(expectedCallData); | ||
|
||
renderHook(() => | ||
useSendTransaction({ | ||
recipientAddress: mockRecipientAddress, | ||
token: { | ||
...mockToken, | ||
symbol: 'USDC', | ||
address: mockToken.address, | ||
decimals: 6, | ||
}, | ||
amount: '100', | ||
}), | ||
); | ||
expect(buildSendTransaction).toHaveBeenCalledWith({ | ||
recipientAddress: mockRecipientAddress, | ||
tokenAddress: mockToken.address, | ||
amount: parseUnits('100', 6), | ||
}); | ||
}); | ||
|
||
it('handles different decimal places correctly', () => { | ||
const mockDecimals = 12; | ||
const expectedCallData = { | ||
to: mockRecipientAddress, | ||
data: mockToken.address, | ||
value: parseUnits('0.5', mockDecimals), | ||
}; | ||
(buildSendTransaction as Mock).mockReturnValue(expectedCallData); | ||
|
||
renderHook(() => | ||
useSendTransaction({ | ||
recipientAddress: mockRecipientAddress, | ||
token: { | ||
...mockToken, | ||
symbol: 'TEST', | ||
address: mockToken.address, | ||
decimals: mockDecimals, | ||
}, | ||
amount: '0.5', | ||
}), | ||
); | ||
expect(buildSendTransaction).toHaveBeenCalledWith({ | ||
recipientAddress: mockRecipientAddress, | ||
tokenAddress: mockToken.address, | ||
amount: parseUnits('0.5', mockDecimals), | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { buildSendTransaction } from '@/api/buildSendTransaction'; | ||
import type { BuildSendTransactionResponse } from '@/api/types'; | ||
import type { Token } from '@/token'; | ||
import { type Address, parseUnits } from 'viem'; | ||
|
||
type UseSendTransactionParams = { | ||
recipientAddress: Address; | ||
token: Token | null; | ||
amount: string; | ||
}; | ||
|
||
export function useSendTransaction({ | ||
recipientAddress, | ||
token, | ||
amount, | ||
}: UseSendTransactionParams): BuildSendTransactionResponse { | ||
if (!token) { | ||
return { | ||
code: 'AmBSeTx01', // Api Module Build Send Transaction Error 01 | ||
error: 'No token provided', | ||
message: 'Could not build send transaction', | ||
}; | ||
} | ||
|
||
if (!token.address) { | ||
if (token.symbol !== 'ETH') { | ||
return { | ||
code: 'AmBSeTx02', // Api Module Build Send Transaction Error 02 | ||
error: 'No token address provided for non-ETH token', | ||
message: 'Could not build send transaction', | ||
}; | ||
} | ||
const parsedAmount = parseUnits(amount, token.decimals); | ||
const sendTransaction = buildSendTransaction({ | ||
recipientAddress, | ||
tokenAddress: null, | ||
amount: parsedAmount, | ||
}); | ||
return sendTransaction; | ||
} | ||
|
||
const parsedAmount = parseUnits(amount, token.decimals); | ||
const sendTransaction = buildSendTransaction({ | ||
recipientAddress, | ||
tokenAddress: token.address, | ||
amount: parsedAmount, | ||
}); | ||
|
||
return sendTransaction; | ||
} |
Oops, something went wrong.