Merge pull request #66 from gnosis/development
PoC: Use EIP-712 signedTypedData in Web3's HttpProvideras signer with PersonalEdition contracts
This commit is contained in:
commit
f355ae1b26
|
@ -1,10 +1,16 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { TX_SERVICE_HOST, ENABLED_TX_SERVICE_MODULES, ENABLED_TX_SERVICE_REMOVAL_SENDER } from '~/config/names'
|
import {
|
||||||
|
TX_SERVICE_HOST,
|
||||||
|
ENABLED_TX_SERVICE_MODULES,
|
||||||
|
ENABLED_TX_SERVICE_REMOVAL_SENDER,
|
||||||
|
SIGNATURES_VIA_METAMASK,
|
||||||
|
} from '~/config/names'
|
||||||
|
|
||||||
const devConfig = {
|
const devConfig = {
|
||||||
[TX_SERVICE_HOST]: 'http://localhost:8000/api/v1/',
|
[TX_SERVICE_HOST]: 'http://localhost:8000/api/v1/',
|
||||||
[ENABLED_TX_SERVICE_MODULES]: false,
|
[ENABLED_TX_SERVICE_MODULES]: false,
|
||||||
[ENABLED_TX_SERVICE_REMOVAL_SENDER]: false,
|
[ENABLED_TX_SERVICE_REMOVAL_SENDER]: false,
|
||||||
|
[SIGNATURES_VIA_METAMASK]: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default devConfig
|
export default devConfig
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { ensureOnce } from '~/utils/singleton'
|
import { ensureOnce } from '~/utils/singleton'
|
||||||
import { TX_SERVICE_HOST, ENABLED_TX_SERVICE_MODULES, ENABLED_TX_SERVICE_REMOVAL_SENDER } from '~/config/names'
|
import {
|
||||||
|
TX_SERVICE_HOST,
|
||||||
|
ENABLED_TX_SERVICE_MODULES,
|
||||||
|
ENABLED_TX_SERVICE_REMOVAL_SENDER,
|
||||||
|
SIGNATURES_VIA_METAMASK,
|
||||||
|
} from '~/config/names'
|
||||||
import devConfig from './development'
|
import devConfig from './development'
|
||||||
import testConfig from './testing'
|
import testConfig from './testing'
|
||||||
import prodConfig from './production'
|
import prodConfig from './production'
|
||||||
|
@ -38,3 +43,9 @@ export const allowedRemoveSenderInTxHistoryService = () => {
|
||||||
|
|
||||||
return config[ENABLED_TX_SERVICE_REMOVAL_SENDER]
|
return config[ENABLED_TX_SERVICE_REMOVAL_SENDER]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const signaturesViaMetamask = () => {
|
||||||
|
const config = getConfig()
|
||||||
|
|
||||||
|
return config[SIGNATURES_VIA_METAMASK]
|
||||||
|
}
|
||||||
|
|
|
@ -3,3 +3,4 @@
|
||||||
export const TX_SERVICE_HOST = 'tsh'
|
export const TX_SERVICE_HOST = 'tsh'
|
||||||
export const ENABLED_TX_SERVICE_MODULES = 'tsm'
|
export const ENABLED_TX_SERVICE_MODULES = 'tsm'
|
||||||
export const ENABLED_TX_SERVICE_REMOVAL_SENDER = 'trs'
|
export const ENABLED_TX_SERVICE_REMOVAL_SENDER = 'trs'
|
||||||
|
export const SIGNATURES_VIA_METAMASK = 'svm'
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { TX_SERVICE_HOST, ENABLED_TX_SERVICE_MODULES, ENABLED_TX_SERVICE_REMOVAL_SENDER } from '~/config/names'
|
import {
|
||||||
|
TX_SERVICE_HOST,
|
||||||
|
ENABLED_TX_SERVICE_MODULES,
|
||||||
|
ENABLED_TX_SERVICE_REMOVAL_SENDER,
|
||||||
|
SIGNATURES_VIA_METAMASK,
|
||||||
|
} from '~/config/names'
|
||||||
|
|
||||||
const prodConfig = {
|
const prodConfig = {
|
||||||
[TX_SERVICE_HOST]: 'https://safe-transaction-history.dev.gnosisdev.com/api/v1/',
|
[TX_SERVICE_HOST]: 'https://safe-transaction-history.dev.gnosisdev.com/api/v1/',
|
||||||
[ENABLED_TX_SERVICE_MODULES]: false,
|
[ENABLED_TX_SERVICE_MODULES]: false,
|
||||||
[ENABLED_TX_SERVICE_REMOVAL_SENDER]: false,
|
[ENABLED_TX_SERVICE_REMOVAL_SENDER]: false,
|
||||||
|
[SIGNATURES_VIA_METAMASK]: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default prodConfig
|
export default prodConfig
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { TX_SERVICE_HOST, ENABLED_TX_SERVICE_MODULES, ENABLED_TX_SERVICE_REMOVAL_SENDER } from '~/config/names'
|
import {
|
||||||
|
TX_SERVICE_HOST,
|
||||||
|
ENABLED_TX_SERVICE_MODULES,
|
||||||
|
ENABLED_TX_SERVICE_REMOVAL_SENDER,
|
||||||
|
SIGNATURES_VIA_METAMASK,
|
||||||
|
} from '~/config/names'
|
||||||
|
|
||||||
const testConfig = {
|
const testConfig = {
|
||||||
[TX_SERVICE_HOST]: 'http://localhost:8000/api/v1/',
|
[TX_SERVICE_HOST]: 'http://localhost:8000/api/v1/',
|
||||||
[ENABLED_TX_SERVICE_MODULES]: false,
|
[ENABLED_TX_SERVICE_MODULES]: false,
|
||||||
[ENABLED_TX_SERVICE_REMOVAL_SENDER]: false,
|
[ENABLED_TX_SERVICE_REMOVAL_SENDER]: false,
|
||||||
|
[SIGNATURES_VIA_METAMASK]: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default testConfig
|
export default testConfig
|
||||||
|
|
|
@ -4,10 +4,12 @@ import { ensureOnce } from '~/utils/singleton'
|
||||||
import { getWeb3 } from '~/logic/wallets/getWeb3'
|
import { getWeb3 } from '~/logic/wallets/getWeb3'
|
||||||
import { promisify } from '~/utils/promisify'
|
import { promisify } from '~/utils/promisify'
|
||||||
import GnosisSafeSol from '#/GnosisSafeTeamEdition.json'
|
import GnosisSafeSol from '#/GnosisSafeTeamEdition.json'
|
||||||
|
import GnosisPersonalSafeSol from '#/GnosisSafePersonalEdition.json'
|
||||||
import ProxyFactorySol from '#/ProxyFactory.json'
|
import ProxyFactorySol from '#/ProxyFactory.json'
|
||||||
import CreateAndAddModules from '#/CreateAndAddModules.json'
|
import CreateAndAddModules from '#/CreateAndAddModules.json'
|
||||||
import DailyLimitModule from '#/DailyLimitModule.json'
|
import DailyLimitModule from '#/DailyLimitModule.json'
|
||||||
import { calculateGasOf, calculateGasPrice, EMPTY_DATA } from '~/logic/wallets/ethTransactions'
|
import { calculateGasOf, calculateGasPrice, EMPTY_DATA } from '~/logic/wallets/ethTransactions'
|
||||||
|
import { signaturesViaMetamask } from '~/config'
|
||||||
|
|
||||||
let proxyFactoryMaster
|
let proxyFactoryMaster
|
||||||
let createAndAddModuleMaster
|
let createAndAddModuleMaster
|
||||||
|
@ -30,8 +32,14 @@ function createAndAddModulesData(dataArray) {
|
||||||
return dataArray.reduce((acc, data) => acc + mw.setup.getData(data).substr(74), EMPTY_DATA)
|
return dataArray.reduce((acc, data) => acc + mw.setup.getData(data).substr(74), EMPTY_DATA)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const createGnosisSafeContract = (web3: any) => {
|
const createGnosisSafeContract = (web3: any) => {
|
||||||
|
if (signaturesViaMetamask()) {
|
||||||
|
const gnosisSafe = contract(GnosisPersonalSafeSol)
|
||||||
|
gnosisSafe.setProvider(web3.currentProvider)
|
||||||
|
|
||||||
|
return gnosisSafe
|
||||||
|
}
|
||||||
|
|
||||||
const gnosisSafe = contract(GnosisSafeSol)
|
const gnosisSafe = contract(GnosisSafeSol)
|
||||||
gnosisSafe.setProvider(web3.currentProvider)
|
gnosisSafe.setProvider(web3.currentProvider)
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,9 @@ import { calculateGasOf, checkReceiptStatus, calculateGasPrice } from '~/logic/w
|
||||||
import { type Operation, submitOperation } from '~/logic/safe/safeTxHistory'
|
import { type Operation, submitOperation } from '~/logic/safe/safeTxHistory'
|
||||||
import { getDailyLimitModuleFrom } from '~/logic/contracts/dailyLimitContracts'
|
import { getDailyLimitModuleFrom } from '~/logic/contracts/dailyLimitContracts'
|
||||||
import { getSafeEthereumInstance } from '~/logic/safe/safeFrontendOperations'
|
import { getSafeEthereumInstance } from '~/logic/safe/safeFrontendOperations'
|
||||||
|
import { generateMetamaskSignature, generateTxGasEstimateFrom, estimateDataGas } from '~/logic/safe/safeTxSigner'
|
||||||
|
import { storeSignature, getSignaturesFrom } from '~/utils/localStorage/signatures'
|
||||||
|
import { signaturesViaMetamask } from '~/config'
|
||||||
|
|
||||||
export const approveTransaction = async (
|
export const approveTransaction = async (
|
||||||
safeAddress: string,
|
safeAddress: string,
|
||||||
|
@ -15,6 +18,14 @@ export const approveTransaction = async (
|
||||||
) => {
|
) => {
|
||||||
const gasPrice = await calculateGasPrice()
|
const gasPrice = await calculateGasPrice()
|
||||||
|
|
||||||
|
if (signaturesViaMetamask()) {
|
||||||
|
const safe = await getSafeEthereumInstance(safeAddress)
|
||||||
|
const txGasEstimate = await generateTxGasEstimateFrom(safe, safeAddress, data, to, valueInWei, operation)
|
||||||
|
const signature =
|
||||||
|
await generateMetamaskSignature(safe, safeAddress, sender, to, valueInWei, nonce, data, operation, txGasEstimate)
|
||||||
|
storeSignature(safeAddress, nonce, signature)
|
||||||
|
}
|
||||||
|
|
||||||
const gnosisSafe = await getSafeEthereumInstance(safeAddress)
|
const gnosisSafe = await getSafeEthereumInstance(safeAddress)
|
||||||
const txData = gnosisSafe.contract.approveTransactionWithParameters.getData(to, valueInWei, data, operation, nonce)
|
const txData = gnosisSafe.contract.approveTransactionWithParameters.getData(to, valueInWei, data, operation, nonce)
|
||||||
const gas = await calculateGasOf(txData, sender, safeAddress)
|
const gas = await calculateGasOf(txData, sender, safeAddress)
|
||||||
|
@ -39,6 +50,40 @@ export const executeTransaction = async (
|
||||||
) => {
|
) => {
|
||||||
const gasPrice = await calculateGasPrice()
|
const gasPrice = await calculateGasPrice()
|
||||||
|
|
||||||
|
if (signaturesViaMetamask()) {
|
||||||
|
const safe = await getSafeEthereumInstance(safeAddress)
|
||||||
|
const txGasEstimate = await generateTxGasEstimateFrom(safe, safeAddress, data, to, valueInWei, operation)
|
||||||
|
const signature =
|
||||||
|
await generateMetamaskSignature(safe, safeAddress, sender, to, valueInWei, nonce, data, operation, txGasEstimate)
|
||||||
|
storeSignature(safeAddress, nonce, signature)
|
||||||
|
|
||||||
|
const sigs = getSignaturesFrom(safeAddress, nonce)
|
||||||
|
|
||||||
|
const threshold = await safe.getThreshold()
|
||||||
|
const gas = await estimateDataGas(safe, to, valueInWei, data, operation, txGasEstimate, 0, nonce, Number(threshold))
|
||||||
|
const numOwners = await safe.getOwners()
|
||||||
|
const gasIncludingRemovingStoreUpfront = gas + txGasEstimate + (numOwners.length * 15000)
|
||||||
|
|
||||||
|
const txReceipt = await safe.execTransactionAndPaySubmitter(
|
||||||
|
to,
|
||||||
|
valueInWei,
|
||||||
|
data,
|
||||||
|
operation,
|
||||||
|
txGasEstimate,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
sigs,
|
||||||
|
{ from: sender, gas: gasIncludingRemovingStoreUpfront, gasPrice },
|
||||||
|
)
|
||||||
|
|
||||||
|
const txHash = txReceipt.tx
|
||||||
|
await checkReceiptStatus(txHash)
|
||||||
|
// await submitOperation(safeAddress, to, valueInWei, data, operation, nonce, txHash, sender, 'execution')
|
||||||
|
|
||||||
|
return txHash
|
||||||
|
}
|
||||||
|
|
||||||
const gnosisSafe = await getSafeEthereumInstance(safeAddress)
|
const gnosisSafe = await getSafeEthereumInstance(safeAddress)
|
||||||
const txConfirmationData =
|
const txConfirmationData =
|
||||||
gnosisSafe.contract.execTransactionIfApproved.getData(to, valueInWei, data, operation, nonce)
|
gnosisSafe.contract.execTransactionIfApproved.getData(to, valueInWei, data, operation, nonce)
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
// @flow
|
||||||
|
import { getWeb3 } from '~/logic/wallets/getWeb3'
|
||||||
|
import { promisify } from '~/utils/promisify'
|
||||||
|
import { BigNumber } from 'bignumber.js'
|
||||||
|
import { EMPTY_DATA } from '~/logic/wallets/ethTransactions'
|
||||||
|
import { getSignaturesFrom } from '~/utils/localStorage/signatures'
|
||||||
|
|
||||||
|
const estimateDataGasCosts = (data) => {
|
||||||
|
const reducer = (accumulator, currentValue) => {
|
||||||
|
if (currentValue === EMPTY_DATA) {
|
||||||
|
return accumulator + 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentValue === '00') {
|
||||||
|
return accumulator + 4
|
||||||
|
}
|
||||||
|
|
||||||
|
return accumulator + 68
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.match(/.{2}/g).reduce(reducer, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const estimateDataGas = (
|
||||||
|
safe: any,
|
||||||
|
to: string,
|
||||||
|
valueInWei: number,
|
||||||
|
data: string,
|
||||||
|
operation: number,
|
||||||
|
txGasEstimate: number,
|
||||||
|
gasToken: number,
|
||||||
|
nonce: number,
|
||||||
|
signatureCount: number,
|
||||||
|
) => {
|
||||||
|
// numbers < 256 are 192 -> 31 * 4 + 68
|
||||||
|
// numbers < 65k are 256 -> 30 * 4 + 2 * 68
|
||||||
|
// For signature array length and dataGasEstimate we already calculated
|
||||||
|
// the 0 bytes so we just add 64 for each non-zero byte
|
||||||
|
const gasPrice = 0 // no need to get refund when we submit txs to metamask
|
||||||
|
const signatureCost = signatureCount * (68 + 2176 + 2176) // array count (3 -> r, s, v) * signature count
|
||||||
|
|
||||||
|
const sigs = getSignaturesFrom(safe.address, nonce)
|
||||||
|
const payload = safe.contract.execTransactionAndPaySubmitter
|
||||||
|
.getData(to, valueInWei, data, operation, txGasEstimate, 0, gasPrice, gasToken, sigs)
|
||||||
|
|
||||||
|
let dataGasEstimate = estimateDataGasCosts(payload) + signatureCost
|
||||||
|
if (dataGasEstimate > 65536) {
|
||||||
|
dataGasEstimate += 64
|
||||||
|
} else {
|
||||||
|
dataGasEstimate += 128
|
||||||
|
}
|
||||||
|
return dataGasEstimate + 34000 // Add aditional gas costs (e.g. base tx costs, transfer costs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
export const generateTxGasEstimateFrom = async (
|
||||||
|
safe: any,
|
||||||
|
safeAddress: string,
|
||||||
|
data: string,
|
||||||
|
to: string,
|
||||||
|
valueInWei: number,
|
||||||
|
operation: number,
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const estimateData = safe.contract.requiredTxGas.getData(to, valueInWei, data, operation)
|
||||||
|
const estimateResponse = await promisify(cb => getWeb3().eth.call({
|
||||||
|
to: safeAddress,
|
||||||
|
from: safeAddress,
|
||||||
|
data: estimateData,
|
||||||
|
}, cb))
|
||||||
|
const txGasEstimate = new BigNumber(estimateResponse.substring(138), 16)
|
||||||
|
|
||||||
|
// Add 10k else we will fail in case of nested calls
|
||||||
|
return Promise.resolve(txGasEstimate.toNumber() + 10000)
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
console.log("Error calculating tx gas estimation " + error)
|
||||||
|
return Promise.resolve(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const generateTypedDataFrom = async (
|
||||||
|
safe: any,
|
||||||
|
safeAddress: string,
|
||||||
|
to: string,
|
||||||
|
valueInWei: number,
|
||||||
|
nonce: number,
|
||||||
|
data: string,
|
||||||
|
operation: number,
|
||||||
|
txGasEstimate: number,
|
||||||
|
) => {
|
||||||
|
const txGasToken = 0
|
||||||
|
// const threshold = await safe.getThreshold()
|
||||||
|
// estimateDataGas(safe, to, valueInWei, data, operation, txGasEstimate, txGasToken, nonce, threshold)
|
||||||
|
const dataGasEstimate = 0
|
||||||
|
const gasPrice = 0
|
||||||
|
const typedData = {
|
||||||
|
types: {
|
||||||
|
EIP712Domain: [
|
||||||
|
{
|
||||||
|
type: 'address',
|
||||||
|
name: 'verifyingContract',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
PersonalSafeTx: [
|
||||||
|
{ type: 'address', name: 'to' },
|
||||||
|
{ type: 'uint256', name: 'value' },
|
||||||
|
{ type: 'bytes', name: 'data' },
|
||||||
|
{ type: 'uint8', name: 'operation' },
|
||||||
|
{ type: 'uint256', name: 'safeTxGas' },
|
||||||
|
{ type: 'uint256', name: 'dataGas' },
|
||||||
|
{ type: 'uint256', name: 'gasPrice' },
|
||||||
|
{ type: 'address', name: 'gasToken' },
|
||||||
|
{ type: 'uint256', name: 'nonce' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
domain: {
|
||||||
|
verifyingContract: safeAddress,
|
||||||
|
},
|
||||||
|
primaryType: 'PersonalSafeTx',
|
||||||
|
message: {
|
||||||
|
to,
|
||||||
|
value: valueInWei,
|
||||||
|
data,
|
||||||
|
operation,
|
||||||
|
safeTxGas: txGasEstimate,
|
||||||
|
dataGas: dataGasEstimate,
|
||||||
|
gasPrice,
|
||||||
|
gasToken: txGasToken,
|
||||||
|
nonce,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return typedData
|
||||||
|
}
|
||||||
|
|
||||||
|
export const generateMetamaskSignature = async (
|
||||||
|
safe: any,
|
||||||
|
safeAddress: string,
|
||||||
|
sender: string,
|
||||||
|
to: string,
|
||||||
|
valueInWei: number,
|
||||||
|
nonce: number,
|
||||||
|
data: string,
|
||||||
|
operation: number,
|
||||||
|
txGasEstimate: number,
|
||||||
|
) => {
|
||||||
|
const web3 = getWeb3()
|
||||||
|
const typedData =
|
||||||
|
await generateTypedDataFrom(safe, safeAddress, to, valueInWei, nonce, data, operation, txGasEstimate)
|
||||||
|
const signedTypedData = {
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
method: 'eth_signTypedData',
|
||||||
|
params: [sender, typedData],
|
||||||
|
id: Date.now(),
|
||||||
|
}
|
||||||
|
const txSignedResponse = await promisify(cb => web3.currentProvider.sendAsync(signedTypedData, cb))
|
||||||
|
|
||||||
|
return txSignedResponse.result.replace(EMPTY_DATA, '')
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import { promisify } from '~/utils/promisify'
|
||||||
import { getWeb3 } from '~/logic/wallets/getWeb3'
|
import { getWeb3 } from '~/logic/wallets/getWeb3'
|
||||||
import { safeTransactionsSelector } from '~/routes/safe/store/selectors'
|
import { safeTransactionsSelector } from '~/routes/safe/store/selectors'
|
||||||
import fetchSafe from '~/routes/safe/store/actions/fetchSafe'
|
import fetchSafe from '~/routes/safe/store/actions/fetchSafe'
|
||||||
|
import { signaturesViaMetamask } from '~/config'
|
||||||
import { testTransactionFrom, testSizeOfTransactions } from './utils/historyServiceHelper'
|
import { testTransactionFrom, testSizeOfTransactions } from './utils/historyServiceHelper'
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ describe('Transactions Suite', () => {
|
||||||
const gnosisSafe = await getSafeEthereumInstance(safeAddress)
|
const gnosisSafe = await getSafeEthereumInstance(safeAddress)
|
||||||
const firstTxData = gnosisSafe.contract.addOwnerWithThreshold.getData(accounts[1], 2)
|
const firstTxData = gnosisSafe.contract.addOwnerWithThreshold.getData(accounts[1], 2)
|
||||||
const executor = accounts[0]
|
const executor = accounts[0]
|
||||||
const nonce = Date.now()
|
const nonce = signaturesViaMetamask() ? await gnosisSafe.nonce() : Date.now()
|
||||||
const firstTxHash = await createTransaction(safe, 'Add Owner Second account', safeAddress, 0, nonce, executor, firstTxData)
|
const firstTxHash = await createTransaction(safe, 'Add Owner Second account', safeAddress, 0, nonce, executor, firstTxData)
|
||||||
await store.dispatch(fetchSafe(safe))
|
await store.dispatch(fetchSafe(safe))
|
||||||
safe = getSafeFrom(store.getState(), safeAddress)
|
safe = getSafeFrom(store.getState(), safeAddress)
|
||||||
|
|
|
@ -4,6 +4,9 @@ import abi from 'ethereumjs-abi'
|
||||||
import { promisify } from '~/utils/promisify'
|
import { promisify } from '~/utils/promisify'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
console.log(`to[${to}] \n\n valieInWei[${valueInWei}] \n\n
|
||||||
|
data[${data}] \n\n operation[${operation}] \n\n sigs[${sigs}]`)
|
||||||
|
|
||||||
const gnosisSafe = await getSafeEthereumInstance(address)
|
const gnosisSafe = await getSafeEthereumInstance(address)
|
||||||
await printOutApprove("Remove owner 3", address, await gnosisSafe.getOwners(), tx.get('data'), tx.get('nonce'))
|
await printOutApprove("Remove owner 3", address, await gnosisSafe.getOwners(), tx.get('data'), tx.get('nonce'))
|
||||||
const txData =
|
const txData =
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
// @flow
|
||||||
|
import { Map } from 'immutable'
|
||||||
|
import { load } from '~/utils/localStorage'
|
||||||
|
|
||||||
|
const getSignaturesKeyFrom = (safeAddress: string) => `TXS-SIGNATURES-${safeAddress}`
|
||||||
|
|
||||||
|
export const storeSignature = (safeAddress: string, nonce: number, signature: string) => {
|
||||||
|
const signaturesKey = getSignaturesKeyFrom(safeAddress)
|
||||||
|
const subjects = Map(load(signaturesKey)) || Map()
|
||||||
|
|
||||||
|
try {
|
||||||
|
const key = `${nonce}`
|
||||||
|
const existingSignatures = subjects.get(key)
|
||||||
|
const signatures = existingSignatures ? existingSignatures + signature : signature
|
||||||
|
const updatedSubjects = subjects.set(key, signatures)
|
||||||
|
const serializedState = JSON.stringify(updatedSubjects)
|
||||||
|
localStorage.setItem(signaturesKey, serializedState)
|
||||||
|
} catch (err) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
console.log('Error storing signatures in localstorage')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSignaturesFrom = (safeAddress: string, nonce: number) => {
|
||||||
|
const key = getSignaturesKeyFrom(safeAddress)
|
||||||
|
const data: any = load(key)
|
||||||
|
|
||||||
|
const signatures = data ? Map(data) : Map()
|
||||||
|
const txSigs = signatures.get(String(nonce)) || ''
|
||||||
|
|
||||||
|
return `0x${txSigs}`
|
||||||
|
}
|
Loading…
Reference in New Issue