Skip to content
This repository has been archived by the owner on Nov 10, 2023. It is now read-only.

Use Gateway SDK for most of the gateway calls #2522

Merged
merged 27 commits into from
Jul 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ yalc.lock
# testing
/coverage/
src/types/contracts/
src/types/gateway/
tsconfig.tsbuildinfo
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"build-desktop": "cross-env REACT_APP_BUILD_FOR_DESKTOP=true REACT_APP_ENV=production yarn build-mainnet",
"build-mainnet": "cross-env REACT_APP_NETWORK=mainnet yarn build",
"build": "cross-env REACT_APP_APP_VERSION=$npm_package_version rescripts --max-old-space-size=8192 build",
"copy-gateway-types": "rm -rf src/types/gateway; cp -r node_modules/@gnosis.pm/safe-react-gateway-sdk/dist/types src/types/gateway",
"eject": "rescripts eject",
"electron-build": "electron-builder --mac --windows --linux",
"electron-dev": "concurrently \"BROWSER=none yarn start\" \"wait-on http://localhost:3000 && electron .\"",
Expand All @@ -31,7 +32,7 @@
"generate-types:safeDeployments": "cross-env typechain --target=web3-v1 --out-dir './src/types/contracts' ./node_modules/@gnosis.pm/safe-deployments/dist/assets/**/*.json",
"lint:check": "eslint './src/**/*.{js,jsx,ts,tsx}'",
"lint:fix": "yarn lint:check --fix",
"postinstall": "patch-package && electron-builder install-app-deps && yarn generate-types",
"postinstall": "patch-package && electron-builder install-app-deps && yarn generate-types && yarn copy-gateway-types",
"preelectron-pack": "yarn build",
"prettier:check": "yarn prettier --check",
"prettier:fix": "yarn prettier --write",
Expand Down Expand Up @@ -145,7 +146,7 @@
}
},
"resolutions": {
"@babel/core": "^7.12.0",
"@babel/core": "^7.14.0",
"sass-loader": "^9.0.0"
},
"browserslist": {
Expand All @@ -166,6 +167,7 @@
"@gnosis.pm/safe-contracts": "1.1.1-dev.2",
"@gnosis.pm/safe-deployments": "^1.0.0",
"@gnosis.pm/safe-react-components": "^0.7.0",
"@gnosis.pm/safe-react-gateway-sdk": "^1.0.0",
"@ledgerhq/hw-transport-node-hid-singleton": "5.51.1",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.11.0",
Expand Down Expand Up @@ -269,7 +271,7 @@
"electron": "^9.4.0",
"electron-builder": "22.10.5",
"electron-notarize": "1.0.0",
"eslint": "^7.29.0",
"eslint": "^7.30.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsx-a11y": "^6.3.1",
Expand All @@ -284,7 +286,7 @@
"redux-mock-store": "^1.5.4",
"sass": "^1.32.0",
"typechain": "^5.1.2",
"typescript": "4.3.4",
"typescript": "^4.3.5",
"wait-on": "^5.3.0"
}
}
2 changes: 0 additions & 2 deletions src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,6 @@ export const getSafeAppsRpcServiceUrl = (): string =>
export const getRpcServiceUrl = (): string =>
usesInfuraRPC ? `${getConfig().rpcServiceUrl}/${INFURA_TOKEN}` : getConfig().rpcServiceUrl

export const getSafeClientGatewayBaseUrl = (safeAddress: string) => `${getClientGatewayUrl()}/safes/${safeAddress}`

export const getTxDetailsUrl = (clientGatewayTxId: string) =>
`${getClientGatewayUrl()}/transactions/${clientGatewayTxId}`

Expand Down
4 changes: 2 additions & 2 deletions src/logic/collectibles/sources/Gnosis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ class Gnosis {

try {
const tokens = await fetchSafeCollectibles(safeAddress)
collectibles.erc721Assets = this._getAssetsFromTokens(tokens.data)
collectibles.erc721Tokens = tokens.data || []
collectibles.erc721Assets = this._getAssetsFromTokens(tokens)
collectibles.erc721Tokens = tokens || []
} catch (error) {
logError(Errors._604, error.message)
}
Expand Down
8 changes: 3 additions & 5 deletions src/logic/currencyValues/api/fetchAvailableCurrencies.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { getFiatCurrencies, GatewayDefinitions } from '@gnosis.pm/safe-react-gateway-sdk'
import { getClientGatewayUrl } from 'src/config'
import axios from 'axios'

export const fetchAvailableCurrencies = async (): Promise<string[]> => {
const url = `${getClientGatewayUrl()}/balances/supported-fiat-codes`

return axios.get(url).then(({ data }) => data)
export const fetchAvailableCurrencies = async (): Promise<GatewayDefinitions['FiatCurrencies']> => {
return getFiatCurrencies(getClientGatewayUrl())
}
69 changes: 16 additions & 53 deletions src/logic/safe/api/__tests__/fetchTokenCurrenciesBalances.test.ts
Original file line number Diff line number Diff line change
@@ -1,66 +1,29 @@
import axios from 'axios'

import { getSafeClientGatewayBaseUrl } from 'src/config'
import { fetchTokenCurrenciesBalances } from 'src/logic/safe/api/fetchTokenCurrenciesBalances'
import { getBalances } from '@gnosis.pm/safe-react-gateway-sdk'

jest.mock('@gnosis.pm/safe-react-gateway-sdk', () => ({
getBalances: jest.fn(() => Promise.resolve({ success: true })),
}))

jest.mock('axios')
describe('fetchTokenCurrenciesBalances', () => {
const safeAddress = '0xdfA693da0D16F5E7E78FdCBeDe8FC6eBEa44f1Cf'
const excludeSpamTokens = true

afterAll(() => {
jest.unmock('axios')
})

it('Given a safe address, calls the API and returns token balances', async () => {
// given
const expectedResult = {
fiatTotal: '104.32679999999999',
items: [
{
tokenInfo: {
type: 'ERC20',
address: '0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e',
decimals: 18,
symbol: 'YFI',
name: 'yearn.finance',
logoUri: 'https://gnosis-safe-token-logos.s3.amazonaws.com/0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e.png',
},
balance: '465000000000000',
fiatBalance: '24.0178',
fiatConversion: '51651.1013',
},
{
tokenInfo: {
type: 'ETHER',
address: '0x0000000000000000000000000000000000000000',
decimals: 18,
symbol: 'ETH',
name: 'Ether',
logoUri: null,
},
balance: '4035779634142020',
fiatBalance: '10.9702',
fiatConversion: '2718.2447',
},
],
}

const apiUrl = getSafeClientGatewayBaseUrl(safeAddress)

// @ts-expect-error mocking get method
axios.get.mockImplementationOnce(() => Promise.resolve({ data: expectedResult }))

// when
const result = await fetchTokenCurrenciesBalances({
it('Given a safe address, calls the API and returns token balances', () => {
fetchTokenCurrenciesBalances({
safeAddress,
excludeSpamTokens,
selectedCurrency: 'USD',
})

// then
expect(result).toStrictEqual(expectedResult)
expect(axios.get).toHaveBeenCalled()
expect(axios.get).toBeCalledWith(`${apiUrl}/balances/USD/?trusted=false&exclude_spam=${excludeSpamTokens}`)
expect(getBalances).toHaveBeenCalledWith(
'http://localhost:8001/v1',
'0xdfA693da0D16F5E7E78FdCBeDe8FC6eBEa44f1Cf',
'USD',
{
exclude_spam: true,
trusted: false,
},
)
})
})
25 changes: 10 additions & 15 deletions src/logic/safe/api/fetchTokenCurrenciesBalances.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import axios from 'axios'

import { getSafeClientGatewayBaseUrl } from 'src/config'
import { TokenProps } from 'src/logic/tokens/store/model/token'
import { getBalances, GatewayDefinitions } from '@gnosis.pm/safe-react-gateway-sdk'
import { getClientGatewayUrl } from 'src/config'
import { checksumAddress } from 'src/utils/checksumAddress'

export type TokenBalance = {
tokenInfo: Omit<TokenProps, 'balance'>
tokenInfo: GatewayDefinitions['TokenInfo']
balance: string
fiatBalance: string
fiatConversion: string
}

export type BalanceEndpoint = {
fiatTotal: string
items: TokenBalance[]
}
export type BalanceEndpoint = GatewayDefinitions['SafeBalanceResponse']

type FetchTokenCurrenciesBalancesProps = {
safeAddress: string
Expand All @@ -23,15 +18,15 @@ type FetchTokenCurrenciesBalancesProps = {
trustedTokens?: boolean
}

export const fetchTokenCurrenciesBalances = ({
export const fetchTokenCurrenciesBalances = async ({
safeAddress,
selectedCurrency,
excludeSpamTokens = true,
trustedTokens = false,
}: FetchTokenCurrenciesBalancesProps): Promise<BalanceEndpoint> => {
const url = `${getSafeClientGatewayBaseUrl(
checksumAddress(safeAddress),
)}/balances/${selectedCurrency}/?trusted=${trustedTokens}&exclude_spam=${excludeSpamTokens}`

return axios.get(url).then(({ data }) => data)
const address = checksumAddress(safeAddress)
return getBalances(getClientGatewayUrl(), address, selectedCurrency, {
exclude_spam: excludeSpamTokens,
trusted: trustedTokens,
})
}
29 changes: 17 additions & 12 deletions src/logic/safe/store/actions/__tests__/fetchSafe.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// --no-ignore
import axios from 'axios'
import { Map } from 'immutable'
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
Expand All @@ -10,22 +9,28 @@ import { SafeRecordProps } from 'src/logic/safe/store/models/safe'
import { UPDATE_SAFE } from 'src/logic/safe/store/actions/updateSafe'
import { DEFAULT_SAFE_INITIAL_STATE } from 'src/logic/safe/store/reducer/safe'
import { inMemoryPartialSafeInformation, localSafesInfo, remoteSafeInfoWithoutModules } from '../mocks/safeInformation'
import * as gateway from '@gnosis.pm/safe-react-gateway-sdk'

jest.mock('@gnosis.pm/safe-react-gateway-sdk', () => ({
__esModule: true,
getSafeInfo: jest.fn(),
}))

jest.mock('axios')
jest.mock('src/utils/storage/index')

describe('buildSafe', () => {
const SAFE_ADDRESS = '0xe414604Ad49602C0b9c0b08D0781ECF96740786a'
const mockedAxios = axios as jest.Mocked<typeof axios>
const mockedGateway = gateway as jest.Mocked<typeof gateway>
const storageUtil = require('src/utils/storage/index') as jest.Mocked<typeof storageUtils>

afterAll(() => {
jest.unmock('axios')
jest.unmock('@gnosis.pm/safe-react-gateway-sdk')
jest.unmock('src/utils/storage/index')
})

// ToDo: use a property other than `name`
it.skip('should return a Partial SafeRecord with a mix of remote and local safe info', async () => {
mockedAxios.get.mockImplementationOnce(async () => ({ data: remoteSafeInfoWithoutModules }))
mockedGateway.getSafeInfo.mockImplementationOnce(async () => remoteSafeInfoWithoutModules as any)
storageUtil.loadFromStorage.mockImplementationOnce(async () => localSafesInfo)
const finalValues: Partial<SafeRecordProps> = {
modules: undefined,
Expand All @@ -38,7 +43,7 @@ describe('buildSafe', () => {
})
it.skip('should return a Partial SafeRecord when `remoteSafeInfo` is not present', async () => {
jest.spyOn(global.console, 'error').mockImplementationOnce(() => {})
mockedAxios.get.mockImplementationOnce(async () => {
mockedGateway.getSafeInfo.mockImplementationOnce(async () => {
throw new Error('-- test -- no resource available')
})
storageUtil.loadFromStorage.mockImplementationOnce(async () => localSafesInfo)
Expand All @@ -48,7 +53,7 @@ describe('buildSafe', () => {
expect(builtSafe).toStrictEqual({ ...inMemoryPartialSafeInformation })
})
it.skip('should return a Partial SafeRecord when `localSafeInfo` is not present', async () => {
mockedAxios.get.mockImplementationOnce(async () => ({ data: remoteSafeInfoWithoutModules }))
mockedGateway.getSafeInfo.mockImplementationOnce(async () => remoteSafeInfoWithoutModules as any)
storageUtil.loadFromStorage.mockImplementationOnce(async () => undefined)

const builtSafe = await buildSafe(SAFE_ADDRESS)
Expand All @@ -73,7 +78,7 @@ describe('buildSafe', () => {
})
it.skip('should return a Partial SafeRecord with only `address` and `name` keys if it fails to recover info', async () => {
jest.spyOn(global.console, 'error').mockImplementationOnce(() => {})
mockedAxios.get.mockImplementationOnce(async () => {
mockedGateway.getSafeInfo.mockImplementationOnce(async () => {
throw new Error('-- test -- no resource available')
})
const finalValues: Partial<SafeRecordProps> = {
Expand All @@ -90,16 +95,16 @@ describe('buildSafe', () => {

describe('fetchSafe', () => {
const SAFE_ADDRESS = '0xe414604Ad49602C0b9c0b08D0781ECF96740786a'
const mockedAxios = axios as jest.Mocked<typeof axios>
const mockedGateway = gateway as jest.Mocked<typeof gateway>
const middlewares = [thunk]
const mockStore = configureMockStore(middlewares)

afterAll(() => {
jest.unmock('axios')
jest.unmock('@gnosis.pm/safe-react-gateway-sdk')
jest.unmock('src/utils/storage/index')
})
it('should create UPDATE_SAFE with remoteSafeInfo', async () => {
mockedAxios.get.mockImplementationOnce(async () => ({ data: remoteSafeInfoWithoutModules }))
mockedGateway.getSafeInfo.mockImplementationOnce(async () => remoteSafeInfoWithoutModules as any)
const expectedActions = [
{
type: UPDATE_SAFE,
Expand Down Expand Up @@ -136,7 +141,7 @@ describe('fetchSafe', () => {
})
it('should dispatch an updateSafe with only `address` if `remoteSafeInfo` is not present', async () => {
jest.spyOn(global.console, 'error').mockImplementationOnce(() => {})
mockedAxios.get.mockImplementationOnce(async () => {
mockedGateway.getSafeInfo.mockImplementationOnce(async () => {
throw new Error('-- test -- no resource available')
})
const expectedActions = [{ type: UPDATE_SAFE, payload: { address: SAFE_ADDRESS } }]
Expand Down
4 changes: 2 additions & 2 deletions src/logic/safe/store/actions/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ describe('extractRemoteSafeInfo', () => {
featuresEnabled: [FEATURES.ERC721, FEATURES.ERC1155, FEATURES.SAFE_APPS, FEATURES.CONTRACT_INTERACTION],
}

const remoteSafeInfo = await extractRemoteSafeInfo(remoteSafeInfoWithoutModules)
const remoteSafeInfo = await extractRemoteSafeInfo(remoteSafeInfoWithoutModules as any)

expect(remoteSafeInfo).toStrictEqual(extractedRemoteSafeInfo)
})
Expand All @@ -208,7 +208,7 @@ describe('extractRemoteSafeInfo', () => {
featuresEnabled: [FEATURES.ERC721, FEATURES.ERC1155, FEATURES.SAFE_APPS, FEATURES.CONTRACT_INTERACTION],
}

const remoteSafeInfo = await extractRemoteSafeInfo(remoteSafeInfoWithModules)
const remoteSafeInfo = await extractRemoteSafeInfo(remoteSafeInfoWithModules as any)

expect(remoteSafeInfo).toStrictEqual(extractedRemoteSafeInfo)
})
Expand Down
12 changes: 7 additions & 5 deletions src/logic/safe/store/actions/processTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { PayableTx } from 'src/types/contracts/types'

import { updateTransactionStatus } from 'src/logic/safe/store/actions/updateTransactionStatus'
import { Confirmation } from 'src/logic/safe/store/models/types/confirmation'
import { Operation } from 'src/logic/safe/store/models/types/gateway.d'
import { Operation, TransactionStatus } from 'src/types/gateway/transactions'
import { isTxPendingError } from 'src/logic/wallets/getWeb3'
import { Errors, logError } from 'src/logic/exceptions/CodedException'

Expand Down Expand Up @@ -122,7 +122,9 @@ export const processTransaction =
if (signature) {
dispatch(closeSnackbarAction({ key: beforeExecutionKey }))

dispatch(updateTransactionStatus({ txStatus: 'PENDING', safeAddress, nonce: tx.nonce, id: tx.id }))
dispatch(
updateTransactionStatus({ txStatus: TransactionStatus.PENDING, safeAddress, nonce: tx.nonce, id: tx.id }),
)
await saveTxToHistory({ ...txArgs, signature })

dispatch(fetchTransactions(safeAddress))
Expand All @@ -148,7 +150,7 @@ export const processTransaction =

dispatch(
updateTransactionStatus({
txStatus: 'PENDING',
txStatus: TransactionStatus.PENDING,
safeAddress,
nonce: tx.nonce,
// if we provide the tx ID that sole tx will have the _pending_ status.
Expand All @@ -171,7 +173,7 @@ export const processTransaction =
.on('error', () => {
dispatch(
updateTransactionStatus({
txStatus: 'PENDING_FAILED',
txStatus: TransactionStatus.PENDING_FAILED,
safeAddress,
nonce: tx.nonce,
id: tx.id,
Expand Down Expand Up @@ -200,7 +202,7 @@ export const processTransaction =

dispatch(
updateTransactionStatus({
txStatus: 'PENDING_FAILED',
txStatus: TransactionStatus.PENDING_FAILED,
safeAddress,
nonce: tx.nonce,
id: tx.id,
Expand Down
Loading