Skip to content

Commit

Permalink
SafeWalletProvider tests
Browse files Browse the repository at this point in the history
  • Loading branch information
katspaugh committed Sep 28, 2023
1 parent c7f0e7a commit ccb8b23
Show file tree
Hide file tree
Showing 2 changed files with 358 additions and 4 deletions.
353 changes: 353 additions & 0 deletions src/services/safe-wallet-provider/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,353 @@
// Unit tests for the SafeWalletProvider class
import { SafeWalletProvider } from '.'

const safe = {
safeAddress: '0x123',
chainId: 1,
}

const appInfo = {
name: 'test',
description: 'test',
iconUrl: 'test',
url: 'test',
}

describe('SafeWalletProvider', () => {
beforeEach(() => {
jest.resetAllMocks()
})

describe('wallet_switchEthereumChain', () => {
it('should call the switchChain method when the method is wallet_switchEthereumChain', async () => {
const switchChain = jest.fn()
const sdk = {
switchChain,
}
const safeWalletProvider = new SafeWalletProvider(safe, sdk as any)

await safeWalletProvider.request(
1,
{ method: 'wallet_switchEthereumChain', params: [{ chainId: '0x1' }] } as any,
{} as any,
)

expect(switchChain).toHaveBeenCalledWith('0x1', {})
})

it('should throw an error when the chain is not supported', async () => {
const sdk = {
switchChain: jest.fn().mockRejectedValue(new Error('Unsupported chain')),
}
const safeWalletProvider = new SafeWalletProvider(safe, sdk as any)

await expect(
safeWalletProvider.request(
1,
{ method: 'wallet_switchEthereumChain', params: [{ chainId: '0x1' }] } as any,
{} as any,
),
).resolves.toEqual({
id: 1,
jsonrpc: '2.0',
error: {
code: -32000,
message: 'Unsupported chain',
},
})
})
})

describe('eth_accounts', () => {
it('should return the safe address when the method is eth_accounts', async () => {
const sdk = {}
const safeWalletProvider = new SafeWalletProvider(safe, sdk as any)

const result = await safeWalletProvider.request(1, { method: 'eth_accounts' } as any, {} as any)

expect(result).toEqual({
id: 1,
jsonrpc: '2.0',
result: ['0x123'],
})
})
})

describe('eth_chainId', () => {
it('should return the chain id when the method is eth_chainId', async () => {
const sdk = {}
const safeWalletProvider = new SafeWalletProvider(safe, sdk as any)

const result = await safeWalletProvider.request(1, { method: 'eth_chainId' } as any, {} as any)

expect(result).toEqual({
id: 1,
jsonrpc: '2.0',
result: '0x1',
})
})
})

describe('eth_sign', () => {
it('should return the signature when the method is eth_sign', async () => {
const sdk = {
signMessage: jest.fn().mockResolvedValue({ signature: '0x123' }),
}
const safeWalletProvider = new SafeWalletProvider(safe, sdk as any)

const result = await safeWalletProvider.request(
1,
{ method: 'eth_sign', params: ['0x123', '0x123'] } as any,
{} as any,
)

expect(result).toEqual({
id: 1,
jsonrpc: '2.0',
result: '0x123',
})
})

it('should throw an error when the address is invalid', async () => {
const sdk = {
signMessage: jest.fn().mockResolvedValue({ signature: '0x123' }),
}

const safeWalletProvider = new SafeWalletProvider(safe, sdk as any)

await expect(
safeWalletProvider.request(1, { method: 'personal_sign', params: ['message', '0x456'] } as any, {} as any),
).resolves.toEqual({
id: 1,
jsonrpc: '2.0',
error: {
code: -32000,
message: 'The address or message hash is invalid',
},
})
})

it('should throw an error when the message hash is invalid', async () => {
const sdk = {
signMessage: jest.fn().mockResolvedValue({ signature: '0x123' }),
}
const safeWalletProvider = new SafeWalletProvider(safe, sdk as any)

await expect(
safeWalletProvider.request(1, { method: 'personal_sign', params: ['message', '123'] } as any, {} as any),
).resolves.toEqual({
id: 1,
jsonrpc: '2.0',
error: {
code: -32000,
message: 'The address or message hash is invalid',
},
})
})

it('should return an empty string when the signature is undefined', async () => {
const sdk = {
signMessage: jest.fn().mockResolvedValue({}),
}
const safeWalletProvider = new SafeWalletProvider(safe, sdk as any)

const result = await safeWalletProvider.request(
1,
{ method: 'personal_sign', params: ['message', '0x123'] } as any,
{} as any,
)

expect(result).toEqual({
id: 1,
jsonrpc: '2.0',
result: '0x',
})
})
})

describe('eth_signTypedData', () => {
it('should return the signature when the method is eth_signTypedData', async () => {
const sdk = {
signTypedMessage: jest.fn().mockResolvedValue({ signature: '0x123' }),
}
const safeWalletProvider = new SafeWalletProvider(safe, sdk as any)

const result = await safeWalletProvider.request(
1,
{
method: 'eth_signTypedData',
params: [
'0x123',
{
domain: {
chainId: 1,
name: 'test',
version: '1',
},
message: {
test: 'test',
},
},
],
} as any,
{} as any,
)

expect(result).toEqual({
id: 1,
jsonrpc: '2.0',
result: '0x123',
})
})

it('should throw an error when the address is invalid', async () => {
const sdk = {
signTypedMessage: jest.fn().mockResolvedValue({ signature: '0x123' }),
}
const safeWalletProvider = new SafeWalletProvider(safe, sdk as any)

await expect(
safeWalletProvider.request(1, { method: 'eth_signTypedData', params: ['0x456', {}] } as any, {} as any),
).resolves.toEqual({
id: 1,
jsonrpc: '2.0',
error: {
code: -32000,
message: 'The address is invalid',
},
})
})
})

describe('eth_sendTransaction', () => {
it('should return the transaction hash when the method is eth_sendTransaction', async () => {
const sdk = {
send: jest.fn().mockResolvedValue({ safeTxHash: '0x456' }),
}
const safeWalletProvider = new SafeWalletProvider(safe, sdk as any)

const result = await safeWalletProvider.request(
1,
{ method: 'eth_sendTransaction', params: [{ from: '0x123', to: '0x123', value: '0x123', gas: 1000 }] } as any,
appInfo,
)

expect(sdk.send).toHaveBeenCalledWith(
{ txs: [{ from: '0x123', to: '0x123', value: '0x123', gas: 1000, data: '0x' }], params: { safeTxGas: 1000 } },
appInfo,
)

expect(result).toEqual({
id: 1,
jsonrpc: '2.0',
result: '0x456',
})
})

it('should throw an error when the transaction is not signed by the safe', async () => {
const sdk = {
send: jest.fn().mockRejectedValue(new Error('User rejected the transaction')),
}
const safeWalletProvider = new SafeWalletProvider(safe, sdk as any)

await expect(
safeWalletProvider.request(
1,
{ method: 'eth_sendTransaction', params: [{ from: '0x123', to: '0x123', value: '0x123' }] } as any,
appInfo,
),
).resolves.toEqual({
id: 1,
jsonrpc: '2.0',
error: {
code: -32000,
message: 'User rejected the transaction',
},
})
})
})

describe('eth_getTransactionByHash', () => {
it('should return the transaction when the method is eth_getTransactionByHash', async () => {
const sdk = {
getBySafeTxHash: jest.fn().mockResolvedValue({ txHash: '0x777' }),
proxy: jest.fn().mockResolvedValue({ hash: '0x999' }),
}
const safeWalletProvider = new SafeWalletProvider(safe, sdk as any)

const result = await safeWalletProvider.request(
1,
{ method: 'eth_getTransactionByHash', params: ['0x123'] } as any,
appInfo,
)

expect(result).toEqual({
id: 1,
jsonrpc: '2.0',
result: { hash: '0x999' },
})
})

it('should send a transaction and return the transaction when it is in the submitted transactions', async () => {
const sdk = {
send: jest.fn().mockResolvedValue({ safeTxHash: '0x777' }),
getBySafeTxHash: jest.fn().mockResolvedValue({ txHash: '0x777' }),
proxy: jest.fn().mockResolvedValue({ hash: '0x999' }),
}
const safeWalletProvider = new SafeWalletProvider(safe, sdk as any)

// Send the transaction
await safeWalletProvider.request(
1,
{ method: 'eth_sendTransaction', params: [{ from: '0x123', to: '0x123', value: '0x123', gas: 1000 }] } as any,
appInfo,
)

const result = await safeWalletProvider.request(
1,
{ method: 'eth_getTransactionByHash', params: ['0x777'] } as any,
appInfo,
)

expect(result).toEqual({
id: 1,
jsonrpc: '2.0',
result: {
blockHash: null,
blockNumber: null,
from: '0x123',
gas: 0,
gasPrice: '0x00',
hash: '0x777',
input: '0x',
nonce: 0,
to: '0x123',
transactionIndex: null,
value: '0x123',
},
})
})
})

describe('eth_getTransactionReceipt', () => {
it('should return the transaction receipt when the method is eth_getTransactionReceipt', async () => {
const sdk = {
getBySafeTxHash: jest.fn().mockResolvedValue({ txHash: '0x777' }),
proxy: jest.fn().mockResolvedValue({ hash: '0x999' }),
}
const safeWalletProvider = new SafeWalletProvider(safe, sdk as any)

const result = await safeWalletProvider.request(
1,
{ method: 'eth_getTransactionReceipt', params: ['0x123'] } as any,
appInfo,
)

expect(result).toEqual({
id: 1,
jsonrpc: '2.0',
result: { hash: '0x999' },
})
})
})
})
9 changes: 5 additions & 4 deletions src/services/safe-wallet-provider/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export class SafeWalletProvider {
tx.gas = parseInt(tx.gas, 16)
}

const resp = await this.sdk.send(
const { safeTxHash } = await this.sdk.send(
{
txs: [tx],
params: { safeTxGas: Number(tx.gas) },
Expand All @@ -133,9 +133,9 @@ export class SafeWalletProvider {
)

// Store fake transaction
this.submittedTxs.set(resp.safeTxHash, {
this.submittedTxs.set(safeTxHash, {
from: this.safe.safeAddress,
hash: resp.safeTxHash,
hash: safeTxHash,
gas: 0,
gasPrice: '0x00',
nonce: 0,
Expand All @@ -147,14 +147,15 @@ export class SafeWalletProvider {
transactionIndex: null,
})

return resp.safeTxHash
return safeTxHash

case 'eth_getTransactionByHash':
let txHash = params[0] as string
try {
const resp = await this.sdk.getBySafeTxHash(txHash)
txHash = resp.txHash || txHash
} catch (e) {}

// Use fake transaction if we don't have a real tx hash
if (this.submittedTxs.has(txHash)) {
return this.submittedTxs.get(txHash)
Expand Down

0 comments on commit ccb8b23

Please sign in to comment.