mirror of
https://github.com/status-im/safe-react.git
synced 2025-01-12 19:14:08 +00:00
Merge pull request #57 from gnosis/feature/integration-tx-history-service
Feature: Send TXs info to Safe Tx History service
This commit is contained in:
commit
cce45b92bd
@ -128,7 +128,7 @@
|
||||
"<rootDir>/src/**/?(*.)(spec|test).js?(x)"
|
||||
],
|
||||
"testEnvironment": "node",
|
||||
"testURL": "https://safe-react",
|
||||
"testURL": "http://localhost:8000",
|
||||
"transform": {
|
||||
"^.+\\.(js|jsx)$": "<rootDir>/node_modules/babel-jest",
|
||||
"^.+\\.(css|scss)$": "<rootDir>/config/jest/cssTransform.js",
|
||||
|
8
src/config/development.js
Normal file
8
src/config/development.js
Normal file
@ -0,0 +1,8 @@
|
||||
// @flow
|
||||
import { TX_SERVICE_HOST } from '~/config/names'
|
||||
|
||||
const devConfig = {
|
||||
[TX_SERVICE_HOST]: 'https://safe-transaction-history.dev.gnosisdev.com/api/v1/',
|
||||
}
|
||||
|
||||
export default devConfig
|
28
src/config/index.js
Normal file
28
src/config/index.js
Normal file
@ -0,0 +1,28 @@
|
||||
// @flow
|
||||
import { ensureOnce } from '~/utils/singleton'
|
||||
import { TX_SERVICE_HOST } from '~/config/names'
|
||||
import devConfig from './development'
|
||||
import testConfig from './testing'
|
||||
import prodConfig from './production'
|
||||
|
||||
const configuration = () => {
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
return testConfig
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
return prodConfig
|
||||
}
|
||||
|
||||
return devConfig
|
||||
}
|
||||
|
||||
const getConfig = ensureOnce(configuration)
|
||||
|
||||
export const getTxServiceHost = () => {
|
||||
const config = getConfig()
|
||||
|
||||
return config[TX_SERVICE_HOST]
|
||||
}
|
||||
|
||||
export const getTxServiceUriFrom = (safeAddress: string) => `safes/${safeAddress}/transactions/`
|
3
src/config/names.js
Normal file
3
src/config/names.js
Normal file
@ -0,0 +1,3 @@
|
||||
// @flow
|
||||
|
||||
export const TX_SERVICE_HOST = 'tsh'
|
8
src/config/production.js
Normal file
8
src/config/production.js
Normal file
@ -0,0 +1,8 @@
|
||||
// @flow
|
||||
import { TX_SERVICE_HOST } from '~/config/names'
|
||||
|
||||
const prodConfig = {
|
||||
[TX_SERVICE_HOST]: 'https://safe-transaction-history.dev.gnosisdev.com/api/v1/',
|
||||
}
|
||||
|
||||
export default prodConfig
|
8
src/config/testing.js
Normal file
8
src/config/testing.js
Normal file
@ -0,0 +1,8 @@
|
||||
// @flow
|
||||
import { TX_SERVICE_HOST } from '~/config/names'
|
||||
|
||||
const testConfig = {
|
||||
[TX_SERVICE_HOST]: 'http://localhost:8000/api/v1/',
|
||||
}
|
||||
|
||||
export default testConfig
|
@ -8,8 +8,9 @@ import { getGnosisSafeContract } from '~/wallets/safeContracts'
|
||||
import { getWeb3 } from '~/wallets/getWeb3'
|
||||
import { type Safe } from '~/routes/safe/store/model/safe'
|
||||
import { sameAddress } from '~/wallets/ethAddresses'
|
||||
import { checkReceiptStatus, calculateGasOf, calculateGasPrice, EMPTY_DATA } from '~/wallets/ethTransactions'
|
||||
import { EMPTY_DATA } from '~/wallets/ethTransactions'
|
||||
import { storeSubject } from '~/utils/localStorage/transactions'
|
||||
import { executeTransaction, approveTransaction } from '~/wallets/safeOperations'
|
||||
|
||||
export const TX_NAME_PARAM = 'txName'
|
||||
export const TX_DESTINATION_PARAM = 'txDestination'
|
||||
@ -93,39 +94,34 @@ export const getSafeEthereumInstance = async (safeAddress: string) => {
|
||||
|
||||
export const createTransaction = async (
|
||||
safe: Safe,
|
||||
txName: string,
|
||||
txDest: string,
|
||||
txValue: number,
|
||||
name: string,
|
||||
to: string,
|
||||
value: number,
|
||||
nonce: number,
|
||||
user: string,
|
||||
sender: string,
|
||||
data: string = EMPTY_DATA,
|
||||
) => {
|
||||
const web3 = getWeb3()
|
||||
const owners = safe.get('owners')
|
||||
const safeAddress = safe.get('address')
|
||||
const gnosisSafe = await getSafeEthereumInstance(safeAddress)
|
||||
const valueInWei = web3.toWei(txValue, 'ether')
|
||||
const threshold = safe.get('threshold')
|
||||
const valueInWei = web3.toWei(value, 'ether')
|
||||
const CALL = 0
|
||||
const gasPrice = await calculateGasPrice()
|
||||
|
||||
const thresholdIsOne = safe.get('threshold') === 1
|
||||
if (hasOneOwner(safe) || thresholdIsOne) {
|
||||
const txConfirmationData =
|
||||
gnosisSafe.contract.execTransactionIfApproved.getData(txDest, valueInWei, data, CALL, nonce)
|
||||
const gas = await calculateGasOf(txConfirmationData, user, safeAddress)
|
||||
const txHash =
|
||||
await gnosisSafe.execTransactionIfApproved(txDest, valueInWei, data, CALL, nonce, { from: user, gas, gasPrice })
|
||||
await checkReceiptStatus(txHash.tx)
|
||||
const executedConfirmations: List<Confirmation> = buildExecutedConfirmationFrom(safe.get('owners'), user)
|
||||
return storeTransaction(txName, nonce, txDest, txValue, user, executedConfirmations, txHash.tx, safeAddress, safe.get('threshold'), data)
|
||||
const isExecution = hasOneOwner(safe) || threshold === 1
|
||||
if (isExecution) {
|
||||
const txHash = await executeTransaction(safeAddress, to, valueInWei, data, CALL, nonce, sender)
|
||||
// TODO Remove when TX History service is fully integrated
|
||||
const executedConfirmations: List<Confirmation> = buildExecutedConfirmationFrom(owners, sender)
|
||||
|
||||
// TODO Remove when TX History service is fully integrated
|
||||
return storeTransaction(name, nonce, to, value, sender, executedConfirmations, txHash, safeAddress, threshold, data)
|
||||
}
|
||||
|
||||
const txData = gnosisSafe.contract.approveTransactionWithParameters.getData(txDest, valueInWei, data, CALL, nonce)
|
||||
const gas = await calculateGasOf(txData, user, safeAddress)
|
||||
const txConfirmationHash = await gnosisSafe
|
||||
.approveTransactionWithParameters(txDest, valueInWei, data, CALL, nonce, { from: user, gas, gasPrice })
|
||||
await checkReceiptStatus(txConfirmationHash.tx)
|
||||
const txHash = await approveTransaction(safeAddress, to, valueInWei, data, CALL, nonce, sender)
|
||||
// TODO Remove when TX History service is fully integrated
|
||||
const confirmations: List<Confirmation> = buildConfirmationsFrom(owners, sender, txHash)
|
||||
|
||||
const confirmations: List<Confirmation> = buildConfirmationsFrom(safe.get('owners'), user, txConfirmationHash.tx)
|
||||
|
||||
return storeTransaction(txName, nonce, txDest, txValue, user, confirmations, '', safeAddress, safe.get('threshold'), data)
|
||||
// TODO Remove when TX History service is fully integrated
|
||||
return storeTransaction(name, nonce, to, value, sender, confirmations, '', safeAddress, threshold, data)
|
||||
}
|
||||
|
54
src/wallets/safeOperations.js
Normal file
54
src/wallets/safeOperations.js
Normal file
@ -0,0 +1,54 @@
|
||||
// @flow
|
||||
import { getSafeEthereumInstance } from '~/wallets/createTransactions'
|
||||
import { calculateGasOf, checkReceiptStatus, calculateGasPrice } from '~/wallets/ethTransactions'
|
||||
import { type Operation, submitOperation } from '~/wallets/safeTxHistory'
|
||||
|
||||
export const approveTransaction = async (
|
||||
safeAddress: string,
|
||||
to: string,
|
||||
valueInWei: number,
|
||||
data: string,
|
||||
operation: Operation,
|
||||
nonce: number,
|
||||
sender: string,
|
||||
) => {
|
||||
const gasPrice = await calculateGasPrice()
|
||||
|
||||
const gnosisSafe = await getSafeEthereumInstance(safeAddress)
|
||||
const txData = gnosisSafe.contract.approveTransactionWithParameters.getData(to, valueInWei, data, operation, nonce)
|
||||
const gas = await calculateGasOf(txData, sender, safeAddress)
|
||||
const txReceipt = await gnosisSafe
|
||||
.approveTransactionWithParameters(to, valueInWei, data, operation, nonce, { from: sender, gas, gasPrice })
|
||||
const txHash = txReceipt.tx
|
||||
await checkReceiptStatus(txHash)
|
||||
|
||||
await submitOperation(safeAddress, to, valueInWei, data, operation, nonce, txHash, sender, 'confirmation')
|
||||
|
||||
return txHash
|
||||
}
|
||||
|
||||
export const executeTransaction = async (
|
||||
safeAddress: string,
|
||||
to: string,
|
||||
valueInWei: number,
|
||||
data: string,
|
||||
operation: Operation,
|
||||
nonce: number,
|
||||
sender: string,
|
||||
) => {
|
||||
const gasPrice = await calculateGasPrice()
|
||||
|
||||
const gnosisSafe = await getSafeEthereumInstance(safeAddress)
|
||||
const txConfirmationData =
|
||||
gnosisSafe.contract.execTransactionIfApproved.getData(to, valueInWei, data, operation, nonce)
|
||||
const gas = await calculateGasOf(txConfirmationData, sender, safeAddress)
|
||||
|
||||
const txReceipt =
|
||||
await gnosisSafe.execTransactionIfApproved(to, valueInWei, data, operation, nonce, { from: sender, gas, gasPrice })
|
||||
const txHash = txReceipt.tx
|
||||
await checkReceiptStatus(txHash)
|
||||
|
||||
await submitOperation(safeAddress, to, valueInWei, data, operation, nonce, txHash, sender, 'execution')
|
||||
|
||||
return txHash
|
||||
}
|
72
src/wallets/safeTxHistory.js
Normal file
72
src/wallets/safeTxHistory.js
Normal file
@ -0,0 +1,72 @@
|
||||
// @flow
|
||||
import { getSafeEthereumInstance } from '~/wallets/createTransactions'
|
||||
import { getWeb3 } from '~/wallets/getWeb3'
|
||||
import { getTxServiceUriFrom, getTxServiceHost } from '~/config'
|
||||
|
||||
type Type = 'confirmation' | 'execution'
|
||||
export type Operation = 0 | 1 | 2
|
||||
|
||||
const calculateBodyFrom = async (
|
||||
safeAddress: string,
|
||||
to: string,
|
||||
valueInWei: number,
|
||||
data: string,
|
||||
operation: Operation,
|
||||
nonce: number,
|
||||
transactionHash: string,
|
||||
sender: string,
|
||||
type: Type,
|
||||
) => {
|
||||
const gnosisSafe = await getSafeEthereumInstance(safeAddress)
|
||||
const contractTransactionHash = await gnosisSafe.getTransactionHash(to, valueInWei, data, operation, nonce)
|
||||
|
||||
return JSON.stringify({
|
||||
to: getWeb3().toChecksumAddress(to),
|
||||
value: valueInWei,
|
||||
data,
|
||||
operation,
|
||||
nonce,
|
||||
contractTransactionHash,
|
||||
transactionHash,
|
||||
sender: getWeb3().toChecksumAddress(sender),
|
||||
type,
|
||||
})
|
||||
}
|
||||
const buildTxServiceUrlFrom = (safeAddress: string) => {
|
||||
const host = getTxServiceHost()
|
||||
const address = getWeb3().toChecksumAddress(safeAddress)
|
||||
const base = getTxServiceUriFrom(address)
|
||||
return `${host}${base}`
|
||||
}
|
||||
|
||||
export const submitOperation = async (
|
||||
safeAddress: string,
|
||||
to: string,
|
||||
valueInWei: number,
|
||||
data: string,
|
||||
operation: Operation,
|
||||
nonce: number,
|
||||
txHash: string,
|
||||
sender: string,
|
||||
type: Type,
|
||||
) => {
|
||||
const url = buildTxServiceUrlFrom(safeAddress)
|
||||
const headers = {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
}
|
||||
const body = await calculateBodyFrom(safeAddress, to, valueInWei, data, operation, nonce, txHash, sender, type)
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body,
|
||||
})
|
||||
|
||||
if (response.status !== 202) {
|
||||
return Promise.reject(new Error('Error submitting the transaction'))
|
||||
}
|
||||
|
||||
return Promise.resolve()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user