Skip to content
This repository was archived by the owner on Nov 10, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/logic/collectibles/sources/Gnosis.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { RateLimit } from 'async-sema'

import { Collectibles, NFTAsset, NFTAssets, NFTTokens } from 'src/logic/collectibles/sources/collectibles'
import { Collectibles, NFTAsset, NFTAssets, NFTTokens } from 'src/logic/collectibles/sources/collectibles.d'
import NFTIcon from 'src/routes/safe/components/Balances/assets/nft_icon.png'
import { fetchErc20AndErc721AssetsList, fetchSafeCollectibles } from 'src/logic/tokens/api'
import { TokenResult } from 'src/logic/tokens/api/fetchErc20AndErc721AssetsList'
Expand Down
2 changes: 1 addition & 1 deletion src/logic/collectibles/sources/OpenSea.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { RateLimit } from 'async-sema'
import { getNetworkId } from 'src/config'

import { ETHEREUM_NETWORK } from 'src/config/networks/network.d'
import { Collectibles, NFTAssets, NFTTokens, OpenSeaAssets } from 'src/logic/collectibles/sources/collectibles'
import { Collectibles, NFTAssets, NFTTokens, OpenSeaAssets } from 'src/logic/collectibles/sources/collectibles.d'
import NFTIcon from 'src/routes/safe/components/Balances/assets/nft_icon.png'
import { OPENSEA_API_KEY } from 'src/utils/constants'

Expand Down
2 changes: 1 addition & 1 deletion src/logic/collectibles/store/selectors/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createSelector } from 'reselect'
import { NFTAsset, NFTAssets, NFTTokens } from 'src/logic/collectibles/sources/collectibles'
import { NFTAsset, NFTAssets, NFTTokens } from 'src/logic/collectibles/sources/collectibles.d'

import { AppReduxState } from 'src/store'
import { NFT_ASSETS_REDUCER_ID, NFT_TOKENS_REDUCER_ID } from 'src/logic/collectibles/store/reducer/collectibles'
Expand Down
30 changes: 21 additions & 9 deletions src/logic/contracts/generateBatchRequests.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { web3ReadOnly as web3 } from 'src/logic/wallets/getWeb3'
import { BatchRequest } from 'web3-core'
import { AbiItem } from 'web3-utils'

/**
* Generates a batch request for grouping RPC calls
Expand All @@ -10,23 +12,33 @@ import { web3ReadOnly as web3 } from 'src/logic/wallets/getWeb3'
* @param {array<{ args: [any], method: string, type: 'eth'|undefined } | string>} args.methods - methods to be called
* @returns {Promise<[*]>}
*/
const generateBatchRequests = ({ abi, address, batch, context, methods }: any): any => {
const contractInstance: any = new web3.eth.Contract(abi, address)
type MethodsArgsType = Array<string | number>

interface Props {
abi: AbiItem[]
address: string
batch?: BatchRequest
context?: unknown
methods: Array<string | {method: string, type?: string, args: MethodsArgsType }>
}

const generateBatchRequests = <ReturnValues>({ abi, address, batch, context, methods }: Props): Promise<ReturnValues> => {
const contractInstance = new web3.eth.Contract(abi, address)
const localBatch = new web3.BatchRequest()

const values = methods.map((methodObject) => {
let method, type, args = []
let method, type, args: MethodsArgsType = []

if (typeof methodObject === 'string') {
method = methodObject
} else {
;({ method, type, args = [] } = methodObject)
({ method, type, args } = methodObject)
}

return new Promise((resolve) => {
const resolver = (error, result) => {
if (error) {
resolve(null)
resolve()
} else {
resolve(result)
}
Expand All @@ -43,7 +55,8 @@ const generateBatchRequests = ({ abi, address, batch, context, methods }: any):
// If batch was provided add to external batch
batch ? batch.add(request) : localBatch.add(request)
} catch (e) {
resolve(null)
console.error('There was an error trying to batch request from web3.', e)
resolve()
}
})
})
Expand All @@ -54,9 +67,8 @@ const generateBatchRequests = ({ abi, address, batch, context, methods }: any):
// in the outside function where the batch object is created.
!batch && localBatch.execute()

const returnValues = context ? [context, ...values] : values

return Promise.all(returnValues)
// @ts-ignore
return Promise.all([context, ...values])
}

export default generateBatchRequests
35 changes: 32 additions & 3 deletions src/logic/safe/store/actions/__tests__/transactionHelpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,36 @@ describe('calculateTransactionType', () => {
describe('buildTx', () => {
it('Returns a valid transaction', async () => {
// given
const cancelTx1 = makeTransaction()
const cancelTx1 = {
baseGas: 0,
blockNumber: 0,
confirmations: [],
confirmationsRequired: 2,
data: null,
dataDecoded: undefined,
ethGasPrice: '0',
executionDate: null,
executor: '',
fee: '',
gasPrice: '',
gasToken: '',
gasUsed: 0,
isExecuted: false,
isSuccessful: true,
modified: '',
nonce: 0,
operation: 0,
origin: null,
refundReceiver: '',
safe: '',
safeTxGas: 0,
safeTxHash: '',
signatures: '',
submissionDate: null,
to: '',
transactionHash: null,
value: '',
}
const transaction = getMockedTxServiceModel({ to: safeAddress2, value: '0' })
const userAddress = 'address1'
const cancellationTxs = List([cancelTx1])
Expand All @@ -764,7 +793,7 @@ describe('buildTx', () => {
})
const knownTokens = Map<string, Record<TokenProps> & Readonly<TokenProps>>()
knownTokens.set('0x00Df91984582e6e96288307E9c2f20b38C8FeCE9', token)
const outgoingTxs = List([cancelTx1])
const outgoingTxs = [cancelTx1]
const safeInstance = makeSafe({ name: 'LOADED SAFE', address: safeAddress })
const expectedTx = makeTransaction({
baseGas: 0,
Expand Down Expand Up @@ -814,7 +843,7 @@ describe('buildTx', () => {
outgoingTxs,
safe: safeInstance,
tx: transaction,
txCode: null,
txCode: undefined,
})

// then
Expand Down
36 changes: 25 additions & 11 deletions src/logic/safe/store/actions/fetchSafe.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import GnosisSafeSol from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafe.json'
import { List, Set, Map } from 'immutable'
import { Action, Dispatch } from 'redux'
import { AbiItem } from 'web3-utils'

import generateBatchRequests from 'src/logic/contracts/generateBatchRequests'
import { getLocalSafe, getSafeName } from 'src/logic/safe/utils'
Expand All @@ -10,10 +12,8 @@ import addSafeOwner from 'src/logic/safe/store/actions/addSafeOwner'
import removeSafeOwner from 'src/logic/safe/store/actions/removeSafeOwner'
import updateSafe from 'src/logic/safe/store/actions/updateSafe'
import { makeOwner } from 'src/logic/safe/store/models/owner'

import { checksumAddress } from 'src/utils/checksumAddress'
import { ModulePair, SafeOwner, SafeRecordProps } from 'src/logic/safe/store/models/safe'
import { Action, Dispatch } from 'redux'
import { SENTINEL_ADDRESS } from 'src/logic/contracts/safeContracts'
import { AppReduxState } from 'src/store'
import { latestMasterContractVersionSelector } from '../selectors'
Expand All @@ -40,8 +40,8 @@ const buildOwnersFrom = (safeOwners: string[], localSafe?: SafeRecordProps): Lis
return List(ownersList)
}

const buildModulesLinkedList = (modules: string[] | undefined, nextModule: string): Array<ModulePair> | null => {
if (modules?.length) {
const buildModulesLinkedList = (modules?: string[], nextModule?: string): Array<ModulePair> | null => {
if (modules?.length && nextModule) {
return modules.map((moduleAddress, index, modules) => {
const prevModule = modules[index + 1]
return [moduleAddress, prevModule !== undefined ? prevModule : nextModule]
Expand All @@ -58,9 +58,9 @@ export const buildSafe = async (
const safeAddress = checksumAddress(safeAdd)

const safeParams = ['getThreshold', 'nonce', 'VERSION', 'getOwners']
const [[thresholdStr, nonceStr, currentVersion, remoteOwners], localSafe, ethBalance] = await Promise.all([
generateBatchRequests({
abi: GnosisSafeSol.abi,
const [[, thresholdStr, nonceStr, currentVersion, remoteOwners = []], localSafe, ethBalance] = await Promise.all([
generateBatchRequests<[undefined, string | undefined, string | undefined, string | undefined, string[]]>({
abi: GnosisSafeSol.abi as AbiItem[],
address: safeAddress,
methods: safeParams,
}),
Expand All @@ -81,7 +81,7 @@ export const buildSafe = async (
owners,
ethBalance,
nonce,
currentVersion,
currentVersion: currentVersion ?? '',
needsUpdate,
featuresEnabled,
balances: Map(),
Expand All @@ -104,9 +104,23 @@ export const checkAndUpdateSafe = (safeAdd: string) => async (dispatch: Dispatch
// TODO: 100 is an arbitrary large number, to avoid the need for pagination. But pagination must be properly handled
{ method: 'getModulesPaginated', args: [SENTINEL_ADDRESS, 100] },
]
const [[remoteThreshold, remoteNonce, remoteOwners, modules], localSafe] = await Promise.all([
generateBatchRequests({
abi: GnosisSafeSol.abi,
const [[, remoteThreshold, remoteNonce, remoteOwners, modules], localSafe] = await Promise.all([
generateBatchRequests<
[
undefined,
string | undefined,
string | undefined,
string[],
(
| {
array: string[]
next: string
}
| undefined
),
]
>({
abi: GnosisSafeSol.abi as AbiItem[],
address: safeAddress,
methods: safeParams,
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import bn from 'bignumber.js'
import { List, Map } from 'immutable'
import { Transaction, TransactionReceipt } from 'web3-core'
import { AbiItem } from 'web3-utils'

import { getNetworkInfo } from 'src/config'
import generateBatchRequests from 'src/logic/contracts/generateBatchRequests'
import { ALTERNATIVE_TOKEN_ABI } from 'src/logic/tokens/utils/alternativeAbi'
Expand Down Expand Up @@ -53,8 +56,16 @@ const batchIncomingTxsTokenDataRequest = (txs: IncomingTxServiceModel[]) => {
{ method: 'getTransactionReceipt', args: [tx.transactionHash], type: 'eth' },
]

return generateBatchRequests({
abi: ALTERNATIVE_TOKEN_ABI,
return generateBatchRequests<
[
IncomingTxServiceModel,
string | undefined,
string | undefined,
Transaction | undefined,
TransactionReceipt | undefined,
]
>({
abi: ALTERNATIVE_TOKEN_ABI as AbiItem[],
address: tx.tokenAddress,
batch,
context: tx,
Expand All @@ -65,11 +76,11 @@ const batchIncomingTxsTokenDataRequest = (txs: IncomingTxServiceModel[]) => {
batch.execute()

return Promise.all(whenTxsValues).then((txsValues) =>
txsValues.map(([tx, symbol, decimals, { gasPrice }, { gasUsed }]) => [
txsValues.map(([tx, symbol, decimals, ethTx, ethTxReceipt]) => [
tx,
symbol === null ? nativeCoin.symbol : symbol,
decimals === null ? nativeCoin.decimals : decimals,
new bn(gasPrice).times(gasUsed),
symbol ? symbol : nativeCoin.symbol,
decimals ? decimals : nativeCoin.decimals,
new bn(ethTx?.gasPrice ?? 0).times(ethTxReceipt?.gasUsed ?? 0),
]),
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { fromJS, List, Map } from 'immutable'

import generateBatchRequests from 'src/logic/contracts/generateBatchRequests'
import { TOKEN_REDUCER_ID } from 'src/logic/tokens/store/reducer/tokens'
import { TOKEN_REDUCER_ID, TokenState } from 'src/logic/tokens/store/reducer/tokens'
import { web3ReadOnly } from 'src/logic/wallets/getWeb3'
import { PROVIDER_REDUCER_ID } from 'src/logic/wallets/store/reducer/provider'
import { buildTx, isCancelTransaction } from 'src/logic/safe/store/actions/transactions/utils/transactionHelpers'
Expand Down Expand Up @@ -59,8 +59,8 @@ export type SafeTransactionsType = {
}

export type OutgoingTxs = {
cancellationTxs: any
outgoingTxs: any
cancellationTxs: Record<number, TxServiceModel>
outgoingTxs: TxServiceModel[]
}

export type BatchProcessTxsProps = OutgoingTxs & {
Expand Down Expand Up @@ -97,20 +97,22 @@ const extractCancelAndOutgoingTxs = (safeAddress: string, outgoingTxs: TxService
)
}

type BatchRequestReturnValues = [TxServiceModel, string | undefined]

/**
* Requests Contract's code for all the Contracts the Safe has interacted with
* @param transactions
* @returns {Promise<[Promise<*[]>, Promise<*[]>, Promise<*[]>, Promise<*[]>, Promise<*[]>, Promise<*[]>, Promise<*[]>, Promise<*[]>, Promise<*[]>, Promise<*[]>]>}
*/
const batchRequestContractCode = (transactions: any[]): Promise<any[]> => {
const batchRequestContractCode = (transactions: TxServiceModel[]): Promise<BatchRequestReturnValues[]> => {
if (!transactions || !Array.isArray(transactions)) {
throw new Error('`transactions` must be provided in order to lookup information')
}

const batch = new web3ReadOnly.BatchRequest()

const whenTxsValues = transactions.map((tx) => {
return generateBatchRequests({
return generateBatchRequests<BatchRequestReturnValues>({
abi: [],
address: tx.to,
batch,
Expand Down Expand Up @@ -141,7 +143,7 @@ const batchProcessOutgoingTransactions = async ({
safe,
}: BatchProcessTxsProps): Promise<{
cancel: Record<string, Transaction>
outgoing: Array<Transaction>
outgoing: Transaction[]
}> => {
// cancellation transactions
const cancelTxsValues = Object.values(cancellationTxs)
Expand Down Expand Up @@ -193,9 +195,9 @@ export const loadOutgoingTransactions = async (safeAddress: string): Promise<Saf
return defaultResponse
}

const knownTokens = state[TOKEN_REDUCER_ID]
const currentUser = state[PROVIDER_REDUCER_ID].get('account')
const safe = state[SAFE_REDUCER_ID].getIn(['safes', safeAddress])
const knownTokens: TokenState = state[TOKEN_REDUCER_ID]
const currentUser: string = state[PROVIDER_REDUCER_ID].get('account')
const safe: SafeRecord = state[SAFE_REDUCER_ID].getIn(['safes', safeAddress])

if (!safe) {
return defaultResponse
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { List, Map } from 'immutable'
import { getNetworkInfo } from 'src/config'
import { TOKEN_REDUCER_ID } from 'src/logic/tokens/store/reducer/tokens'
import { TOKEN_REDUCER_ID, TokenState } from 'src/logic/tokens/store/reducer/tokens'
import {
getERC20DecimalsAndSymbol,
getERC721Symbol,
Expand Down Expand Up @@ -78,15 +78,15 @@ export const isUpgradeTransaction = (tx: TxServiceModel): boolean => {
)
}

export const isOutgoingTransaction = (tx: TxServiceModel, safeAddress: string): boolean => {
export const isOutgoingTransaction = (tx: TxServiceModel, safeAddress?: string): boolean => {
return !sameAddress(tx.to, safeAddress) && !isEmptyData(tx.data)
}

export const isCustomTransaction = async (
tx: TxServiceModel,
txCode: string | null,
safeAddress: string,
knownTokens: Map<string, Token>,
txCode?: string,
safeAddress?: string,
knownTokens?: TokenState,
): Promise<boolean> => {
const isOutgoing = isOutgoingTransaction(tx, safeAddress)
const isErc20 = await isSendERC20Transaction(tx, txCode, knownTokens)
Expand Down Expand Up @@ -162,7 +162,7 @@ export const getConfirmations = (tx: TxServiceModel): List<Confirmation> => {
export const isTransactionCancelled = (
tx: TxServiceModel,
outgoingTxs: Array<TxServiceModel>,
cancellationTxs: { number: TxServiceModel },
cancellationTxs: Record<string, TxServiceModel>,
): boolean => {
return (
// not executed
Expand Down Expand Up @@ -231,7 +231,7 @@ export const calculateTransactionType = (tx: Transaction): TransactionTypeValues

export type BuildTx = BatchProcessTxsProps & {
tx: TxServiceModel
txCode: string | null
txCode?: string
}

export const buildTx = async ({
Expand Down
4 changes: 2 additions & 2 deletions src/logic/safe/utils/safeVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ const checkFeatureEnabledByVersion = (featureConfig: FeatureConfigByVersion, ver
return featureConfig.validVersion ? semverSatisfies(version, featureConfig.validVersion) : true
}

export const enabledFeatures = (version: string): FEATURES[] => {
export const enabledFeatures = (version?: string): FEATURES[] => {
const disabledFeatures = getNetworkConfigDisabledFeatures()
return FEATURES_BY_VERSION.reduce((acc: FEATURES[], feature: Feature) => {
if (!disabledFeatures.includes(feature.name) && checkFeatureEnabledByVersion(feature, version)) {
if (!disabledFeatures.includes(feature.name) && version && checkFeatureEnabledByVersion(feature, version)) {
acc.push(feature.name)
}
return acc
Expand Down
Loading