Merge pull request #47 from gnosis/feature/WA-232-custom-tokens
WA-232 Display & Transfer custom tokens
This commit is contained in:
commit
84ffda916c
|
@ -1,7 +1,7 @@
|
|||
import 'babel-polyfill'
|
||||
import { addDecorator, configure } from '@storybook/react'
|
||||
import { withKnobs } from '@storybook/addon-knobs'
|
||||
import { MuiThemeProvider } from 'material-ui/styles'
|
||||
import { MuiThemeProvider } from '@material-ui/core/styles'
|
||||
import * as React from 'react'
|
||||
import { Provider } from 'react-redux'
|
||||
import StoryRouter from 'storybook-router'
|
||||
|
|
|
@ -101,6 +101,7 @@
|
|||
"webpack-manifest-plugin": "^2.0.0-rc.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@gnosis.pm/util-contracts": "^0.2.14",
|
||||
"@material-ui/core": "^1.2.1",
|
||||
"@material-ui/icons": "^1.1.0",
|
||||
"final-form": "^4.2.1",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -52,8 +52,8 @@
|
|||
],
|
||||
"bytecode": "0x608060405234801561001057600080fd5b506040516102fc3803806102fc83398101806040528101908080519060200190929190805182019291905050508160008173ffffffffffffffffffffffffffffffffffffffff16141515156100f3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260248152602001807f496e76616c6964206d617374657220636f707920616464726573732070726f7681526020017f696465640000000000000000000000000000000000000000000000000000000081525060400191505060405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506000815111156101805773ffffffffffffffffffffffffffffffffffffffff60005416600080835160208501846127105a03f46040513d6000823e600082141561017c573d81fd5b5050505b505061016b806101916000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680634555d5c91461008b5780635c60da1b146100b6575b73ffffffffffffffffffffffffffffffffffffffff600054163660008037600080366000845af43d6000803e6000811415610086573d6000fd5b3d6000f35b34801561009757600080fd5b506100a061010d565b6040518082815260200191505060405180910390f35b3480156100c257600080fd5b506100cb610116565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60006002905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050905600a165627a7a72305820bab5357556c704bffef0f96326dd27742408be175057ffd8f4f58237314cfd520029",
|
||||
"deployedBytecode": "0x60806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680634555d5c91461008b5780635c60da1b146100b6575b73ffffffffffffffffffffffffffffffffffffffff600054163660008037600080366000845af43d6000803e6000811415610086573d6000fd5b3d6000f35b34801561009757600080fd5b506100a061010d565b6040518082815260200191505060405180910390f35b3480156100c257600080fd5b506100cb610116565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60006002905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050905600a165627a7a72305820bab5357556c704bffef0f96326dd27742408be175057ffd8f4f58237314cfd520029",
|
||||
"sourceMap": "355:882:0:-;;;610:625;8:9:-1;5:2;;;30:1;27;20:12;5:2;610:625:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;668:11;593:1:11;578:11;:16;;;;570:65;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;658:11;645:10;;:24;;;;;;;;;;;;;;;;;;508:168;735:1:0;714:11;:18;:22;710:519;;;879:42;875:1;869:8;865:57;1043:1;1040;1026:11;1020:18;1013:4;1000:11;996:22;984:10;976:5;971:3;967:15;954:91;1079:4;1073:11;1124:14;1121:1;1116:3;1101:38;1171:1;1162:7;1159:14;1156:2;;;1188:14;1183:3;1176:27;1156:2;829:390;;;;610:625;;355:882;;;;;;",
|
||||
"deployedSourceMap": "355:882:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;955:42:11;951:1;945:8;941:57;1030:14;1027:1;1024;1011:34;1125:1;1122;1106:14;1103:1;1091:10;1086:3;1073:54;1161:16;1158:1;1155;1140:38;1206:1;1197:7;1194:14;1191:2;;;1221:16;1218:1;1211:27;1191:2;1263:16;1260:1;1253:27;1426:104;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1426:104:11;;;;;;;;;;;;;;;;;;;;;;;1302:118;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1302:118:11;;;;;;;;;;;;;;;;;;;;;;;;;;;1426:104;1492:7;1522:1;1515:8;;1426:104;:::o;1302:118::-;1373:7;1403:10;;;;;;;;;;;1396:17;;1302:118;:::o",
|
||||
"sourceMap": "355:882:0:-;;;610:625;8:9:-1;5:2;;;30:1;27;20:12;5:2;610:625:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;668:11;593:1:13;578:11;:16;;;;570:65;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;658:11;645:10;;:24;;;;;;;;;;;;;;;;;;508:168;735:1:0;714:11;:18;:22;710:519;;;879:42;875:1;869:8;865:57;1043:1;1040;1026:11;1020:18;1013:4;1000:11;996:22;984:10;976:5;971:3;967:15;954:91;1079:4;1073:11;1124:14;1121:1;1116:3;1101:38;1171:1;1162:7;1159:14;1156:2;;;1188:14;1183:3;1176:27;1156:2;829:390;;;;610:625;;355:882;;;;;;",
|
||||
"deployedSourceMap": "355:882:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;955:42:13;951:1;945:8;941:57;1030:14;1027:1;1024;1011:34;1125:1;1122;1106:14;1103:1;1091:10;1086:3;1073:54;1161:16;1158:1;1155;1140:38;1206:1;1197:7;1194:14;1191:2;;;1221:16;1218:1;1211:27;1191:2;1263:16;1260:1;1253:27;1426:104;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1426:104:13;;;;;;;;;;;;;;;;;;;;;;;1302:118;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1302:118:13;;;;;;;;;;;;;;;;;;;;;;;;;;;1426:104;1492:7;1522:1;1515:8;;1426:104;:::o;1302:118::-;1373:7;1403:10;;;;;;;;;;;1396:17;;1302:118;:::o",
|
||||
"source": "pragma solidity 0.4.24;\nimport \"./Proxy.sol\";\n\n\n/// @title Delegate Constructor Proxy - Generic proxy contract allows to execute all transactions applying the code of a master contract. It is possible to send along initialization data with the constructor.\n/// @author Stefan George - <stefan@gnosis.pm>\n/// @author Richard Meissner - <richard@gnosis.pm>\ncontract DelegateConstructorProxy is Proxy {\n\n /// @dev Constructor function sets address of master copy contract.\n /// @param _masterCopy Master copy address.\n /// @param initializer Data used for a delegate call to initialize the contract.\n constructor(address _masterCopy, bytes initializer) Proxy(_masterCopy)\n public\n {\n if (initializer.length > 0) {\n // solium-disable-next-line security/no-inline-assembly\n assembly {\n let masterCopy := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)\n let success := delegatecall(sub(gas, 10000), masterCopy, add(initializer, 0x20), mload(initializer), 0, 0)\n let ptr := mload(0x40)\n returndatacopy(ptr, 0, returndatasize)\n if eq(success, 0) { revert(ptr, returndatasize) }\n }\n }\n }\n}\n",
|
||||
"sourcePath": "/Users/apanizo/git/gnosis/safe-contracts/contracts/DelegateConstructorProxy.sol",
|
||||
"ast": {
|
||||
|
@ -82,7 +82,7 @@
|
|||
"id": 2,
|
||||
"nodeType": "ImportDirective",
|
||||
"scope": 24,
|
||||
"sourceUnit": 1612,
|
||||
"sourceUnit": 2989,
|
||||
"src": "24:21:0",
|
||||
"symbolAliases": [],
|
||||
"unitAlias": ""
|
||||
|
@ -96,10 +96,10 @@
|
|||
"id": 3,
|
||||
"name": "Proxy",
|
||||
"nodeType": "UserDefinedTypeName",
|
||||
"referencedDeclaration": 1611,
|
||||
"referencedDeclaration": 2988,
|
||||
"src": "392:5:0",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_contract$_Proxy_$1611",
|
||||
"typeIdentifier": "t_contract$_Proxy_$2988",
|
||||
"typeString": "contract Proxy"
|
||||
}
|
||||
},
|
||||
|
@ -109,7 +109,7 @@
|
|||
}
|
||||
],
|
||||
"contractDependencies": [
|
||||
1611
|
||||
2988
|
||||
],
|
||||
"contractKind": "contract",
|
||||
"documentation": "@title Delegate Constructor Proxy - Generic proxy contract allows to execute all transactions applying the code of a master contract. It is possible to send along initialization data with the constructor.\n @author Stefan George - <stefan@gnosis.pm>\n @author Richard Meissner - <richard@gnosis.pm>",
|
||||
|
@ -117,7 +117,7 @@
|
|||
"id": 23,
|
||||
"linearizedBaseContracts": [
|
||||
23,
|
||||
1611
|
||||
2988
|
||||
],
|
||||
"name": "DelegateConstructorProxy",
|
||||
"nodeType": "ContractDefinition",
|
||||
|
@ -264,10 +264,10 @@
|
|||
"name": "Proxy",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 1611,
|
||||
"referencedDeclaration": 2988,
|
||||
"src": "662:5:0",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_type$_t_contract$_Proxy_$1611_$",
|
||||
"typeIdentifier": "t_type$_t_contract$_Proxy_$2988_$",
|
||||
"typeString": "type(contract Proxy)"
|
||||
}
|
||||
},
|
||||
|
@ -382,7 +382,7 @@
|
|||
"id": 2,
|
||||
"nodeType": "ImportDirective",
|
||||
"scope": 24,
|
||||
"sourceUnit": 1612,
|
||||
"sourceUnit": 2989,
|
||||
"src": "24:21:0",
|
||||
"symbolAliases": [],
|
||||
"unitAlias": ""
|
||||
|
@ -396,10 +396,10 @@
|
|||
"id": 3,
|
||||
"name": "Proxy",
|
||||
"nodeType": "UserDefinedTypeName",
|
||||
"referencedDeclaration": 1611,
|
||||
"referencedDeclaration": 2988,
|
||||
"src": "392:5:0",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_contract$_Proxy_$1611",
|
||||
"typeIdentifier": "t_contract$_Proxy_$2988",
|
||||
"typeString": "contract Proxy"
|
||||
}
|
||||
},
|
||||
|
@ -409,7 +409,7 @@
|
|||
}
|
||||
],
|
||||
"contractDependencies": [
|
||||
1611
|
||||
2988
|
||||
],
|
||||
"contractKind": "contract",
|
||||
"documentation": "@title Delegate Constructor Proxy - Generic proxy contract allows to execute all transactions applying the code of a master contract. It is possible to send along initialization data with the constructor.\n @author Stefan George - <stefan@gnosis.pm>\n @author Richard Meissner - <richard@gnosis.pm>",
|
||||
|
@ -417,7 +417,7 @@
|
|||
"id": 23,
|
||||
"linearizedBaseContracts": [
|
||||
23,
|
||||
1611
|
||||
2988
|
||||
],
|
||||
"name": "DelegateConstructorProxy",
|
||||
"nodeType": "ContractDefinition",
|
||||
|
@ -564,10 +564,10 @@
|
|||
"name": "Proxy",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 1611,
|
||||
"referencedDeclaration": 2988,
|
||||
"src": "662:5:0",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_type$_t_contract$_Proxy_$1611_$",
|
||||
"typeIdentifier": "t_type$_t_contract$_Proxy_$2988_$",
|
||||
"typeString": "type(contract Proxy)"
|
||||
}
|
||||
},
|
||||
|
@ -662,5 +662,5 @@
|
|||
},
|
||||
"networks": {},
|
||||
"schemaVersion": "2.0.0",
|
||||
"updatedAt": "2018-06-18T14:44:09.445Z"
|
||||
"updatedAt": "2018-06-29T09:01:22.054Z"
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -3,29 +3,29 @@
|
|||
"abi": [],
|
||||
"bytecode": "0x6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00a165627a7a72305820641ab8b295edfaa2b1c8a8e0ae7d17ea2f4c8b95ea27e45d8947ed9a4799ca1f0029",
|
||||
"deployedBytecode": "0x6080604052600080fd00a165627a7a72305820641ab8b295edfaa2b1c8a8e0ae7d17ea2f4c8b95ea27e45d8947ed9a4799ca1f0029",
|
||||
"sourceMap": "115:95:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;115:95:0;;;;;;;",
|
||||
"deployedSourceMap": "115:95:0:-;;;;;",
|
||||
"sourceMap": "115:95:1:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;115:95:1;;;;;;;",
|
||||
"deployedSourceMap": "115:95:1:-;;;;;",
|
||||
"source": "pragma solidity 0.4.24;\n\n\n/// @title Enum - Collection of enums\n/// @author Richard Meissner - <richard@gnosis.pm>\ncontract Enum {\n enum Operation {\n Call,\n DelegateCall,\n Create\n }\n}\n",
|
||||
"sourcePath": "/Users/apanizo/git/gnosis/safe-contracts/contracts/Enum.sol",
|
||||
"ast": {
|
||||
"absolutePath": "/Users/apanizo/git/gnosis/safe-contracts/contracts/Enum.sol",
|
||||
"exportedSymbols": {
|
||||
"Enum": [
|
||||
6
|
||||
30
|
||||
]
|
||||
},
|
||||
"id": 7,
|
||||
"id": 31,
|
||||
"nodeType": "SourceUnit",
|
||||
"nodes": [
|
||||
{
|
||||
"id": 1,
|
||||
"id": 25,
|
||||
"literals": [
|
||||
"solidity",
|
||||
"0.4",
|
||||
".24"
|
||||
],
|
||||
"nodeType": "PragmaDirective",
|
||||
"src": "0:23:0"
|
||||
"src": "0:23:1"
|
||||
},
|
||||
{
|
||||
"baseContracts": [],
|
||||
|
@ -33,66 +33,66 @@
|
|||
"contractKind": "contract",
|
||||
"documentation": "@title Enum - Collection of enums\n @author Richard Meissner - <richard@gnosis.pm>",
|
||||
"fullyImplemented": true,
|
||||
"id": 6,
|
||||
"id": 30,
|
||||
"linearizedBaseContracts": [
|
||||
6
|
||||
30
|
||||
],
|
||||
"name": "Enum",
|
||||
"nodeType": "ContractDefinition",
|
||||
"nodes": [
|
||||
{
|
||||
"canonicalName": "Enum.Operation",
|
||||
"id": 5,
|
||||
"id": 29,
|
||||
"members": [
|
||||
{
|
||||
"id": 2,
|
||||
"id": 26,
|
||||
"name": "Call",
|
||||
"nodeType": "EnumValue",
|
||||
"src": "160:4:0"
|
||||
"src": "160:4:1"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"id": 27,
|
||||
"name": "DelegateCall",
|
||||
"nodeType": "EnumValue",
|
||||
"src": "174:12:0"
|
||||
"src": "174:12:1"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"id": 28,
|
||||
"name": "Create",
|
||||
"nodeType": "EnumValue",
|
||||
"src": "196:6:0"
|
||||
"src": "196:6:1"
|
||||
}
|
||||
],
|
||||
"name": "Operation",
|
||||
"nodeType": "EnumDefinition",
|
||||
"src": "135:73:0"
|
||||
"src": "135:73:1"
|
||||
}
|
||||
],
|
||||
"scope": 7,
|
||||
"src": "115:95:0"
|
||||
"scope": 31,
|
||||
"src": "115:95:1"
|
||||
}
|
||||
],
|
||||
"src": "0:211:0"
|
||||
"src": "0:211:1"
|
||||
},
|
||||
"legacyAST": {
|
||||
"absolutePath": "/Users/apanizo/git/gnosis/safe-contracts/contracts/Enum.sol",
|
||||
"exportedSymbols": {
|
||||
"Enum": [
|
||||
6
|
||||
30
|
||||
]
|
||||
},
|
||||
"id": 7,
|
||||
"id": 31,
|
||||
"nodeType": "SourceUnit",
|
||||
"nodes": [
|
||||
{
|
||||
"id": 1,
|
||||
"id": 25,
|
||||
"literals": [
|
||||
"solidity",
|
||||
"0.4",
|
||||
".24"
|
||||
],
|
||||
"nodeType": "PragmaDirective",
|
||||
"src": "0:23:0"
|
||||
"src": "0:23:1"
|
||||
},
|
||||
{
|
||||
"baseContracts": [],
|
||||
|
@ -100,46 +100,46 @@
|
|||
"contractKind": "contract",
|
||||
"documentation": "@title Enum - Collection of enums\n @author Richard Meissner - <richard@gnosis.pm>",
|
||||
"fullyImplemented": true,
|
||||
"id": 6,
|
||||
"id": 30,
|
||||
"linearizedBaseContracts": [
|
||||
6
|
||||
30
|
||||
],
|
||||
"name": "Enum",
|
||||
"nodeType": "ContractDefinition",
|
||||
"nodes": [
|
||||
{
|
||||
"canonicalName": "Enum.Operation",
|
||||
"id": 5,
|
||||
"id": 29,
|
||||
"members": [
|
||||
{
|
||||
"id": 2,
|
||||
"id": 26,
|
||||
"name": "Call",
|
||||
"nodeType": "EnumValue",
|
||||
"src": "160:4:0"
|
||||
"src": "160:4:1"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"id": 27,
|
||||
"name": "DelegateCall",
|
||||
"nodeType": "EnumValue",
|
||||
"src": "174:12:0"
|
||||
"src": "174:12:1"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"id": 28,
|
||||
"name": "Create",
|
||||
"nodeType": "EnumValue",
|
||||
"src": "196:6:0"
|
||||
"src": "196:6:1"
|
||||
}
|
||||
],
|
||||
"name": "Operation",
|
||||
"nodeType": "EnumDefinition",
|
||||
"src": "135:73:0"
|
||||
"src": "135:73:1"
|
||||
}
|
||||
],
|
||||
"scope": 7,
|
||||
"src": "115:95:0"
|
||||
"scope": 31,
|
||||
"src": "115:95:1"
|
||||
}
|
||||
],
|
||||
"src": "0:211:0"
|
||||
"src": "0:211:1"
|
||||
},
|
||||
"compiler": {
|
||||
"name": "solc",
|
||||
|
@ -147,5 +147,5 @@
|
|||
},
|
||||
"networks": {},
|
||||
"schemaVersion": "2.0.0",
|
||||
"updatedAt": "2018-06-20T07:57:26.985Z"
|
||||
"updatedAt": "2018-06-29T09:01:22.054Z"
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -18,38 +18,38 @@
|
|||
],
|
||||
"bytecode": "0x608060405234801561001057600080fd5b50610276806100206000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680637de7edef14610046575b600080fd5b34801561005257600080fd5b50610087600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610089565b005b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610152576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c8152602001807f4d6574686f642063616e206f6e6c792062652063616c6c65642066726f6d207481526020017f68697320636f6e7472616374000000000000000000000000000000000000000081525060400191505060405180910390fd5b60008173ffffffffffffffffffffffffffffffffffffffff1614151515610207576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260248152602001807f496e76616c6964206d617374657220636f707920616464726573732070726f7681526020017f696465640000000000000000000000000000000000000000000000000000000081525060400191505060405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505600a165627a7a72305820243ca7a44eb0464a47c14309cc3a29e407df6e966674981a787df22c0d9280220029",
|
||||
"deployedBytecode": "0x608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680637de7edef14610046575b600080fd5b34801561005257600080fd5b50610087600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610089565b005b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610152576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c8152602001807f4d6574686f642063616e206f6e6c792062652063616c6c65642066726f6d207481526020017f68697320636f6e7472616374000000000000000000000000000000000000000081525060400191505060405180910390fd5b60008173ffffffffffffffffffffffffffffffffffffffff1614151515610207576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260248152602001807f496e76616c6964206d617374657220636f707920616464726573732070726f7681526020017f696465640000000000000000000000000000000000000000000000000000000081525060400191505060405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505600a165627a7a72305820243ca7a44eb0464a47c14309cc3a29e407df6e966674981a787df22c0d9280220029",
|
||||
"sourceMap": "203:673:4:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;203:673:4;;;;;;;",
|
||||
"deployedSourceMap": "203:673:4:-;;;;;;;;;;;;;;;;;;;;;;;;626:248;;8:9:-1;5:2;;;30:1;27;20:12;5:2;626:248:4;;;;;;;;;;;;;;;;;;;;;;;;;;;;;244:4:8;222:27;;:10;:27;;;214:84;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;791:1:4;776:11;:16;;;;768:65;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;856:11;843:10;;:24;;;;;;;;;;;;;;;;;;626:248;:::o",
|
||||
"sourceMap": "203:673:5:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;203:673:5;;;;;;;",
|
||||
"deployedSourceMap": "203:673:5:-;;;;;;;;;;;;;;;;;;;;;;;;626:248;;8:9:-1;5:2;;;30:1;27;20:12;5:2;626:248:5;;;;;;;;;;;;;;;;;;;;;;;;;;;;;244:4:16;222:27;;:10;:27;;;214:84;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;791:1:5;776:11;:16;;;;768:65;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;856:11;843:10;;:24;;;;;;;;;;;;;;;;;;626:248;:::o",
|
||||
"source": "pragma solidity 0.4.24;\nimport \"./SelfAuthorized.sol\";\n\n\n/// @title MasterCopy - Base for master copy contracts (should always be first super contract)\n/// @author Richard Meissner - <richard@gnosis.pm>\ncontract MasterCopy is SelfAuthorized {\n // masterCopy always needs to be first declared variable, to ensure that it is at the same location as in the Proxy contract.\n // It should also always be ensured that the address is stored alone (uses a full word)\n address masterCopy;\n\n /// @dev Allows to upgrade the contract. This can only be done via a Safe transaction.\n /// @param _masterCopy New contract address.\n function changeMasterCopy(address _masterCopy)\n public\n authorized\n {\n // Master copy address cannot be null.\n require(_masterCopy != 0, \"Invalid master copy address provided\");\n masterCopy = _masterCopy;\n }\n}\n",
|
||||
"sourcePath": "/Users/apanizo/git/gnosis/safe-contracts/contracts/MasterCopy.sol",
|
||||
"ast": {
|
||||
"absolutePath": "/Users/apanizo/git/gnosis/safe-contracts/contracts/MasterCopy.sol",
|
||||
"exportedSymbols": {
|
||||
"MasterCopy": [
|
||||
608
|
||||
632
|
||||
]
|
||||
},
|
||||
"id": 609,
|
||||
"id": 633,
|
||||
"nodeType": "SourceUnit",
|
||||
"nodes": [
|
||||
{
|
||||
"id": 583,
|
||||
"id": 607,
|
||||
"literals": [
|
||||
"solidity",
|
||||
"0.4",
|
||||
".24"
|
||||
],
|
||||
"nodeType": "PragmaDirective",
|
||||
"src": "0:23:4"
|
||||
"src": "0:23:5"
|
||||
},
|
||||
{
|
||||
"absolutePath": "/Users/apanizo/git/gnosis/safe-contracts/contracts/SelfAuthorized.sol",
|
||||
"file": "./SelfAuthorized.sol",
|
||||
"id": 584,
|
||||
"id": 608,
|
||||
"nodeType": "ImportDirective",
|
||||
"scope": 609,
|
||||
"sourceUnit": 1450,
|
||||
"src": "24:30:4",
|
||||
"scope": 633,
|
||||
"sourceUnit": 3066,
|
||||
"src": "24:30:5",
|
||||
"symbolAliases": [],
|
||||
"unitAlias": ""
|
||||
},
|
||||
|
@ -59,42 +59,42 @@
|
|||
"arguments": null,
|
||||
"baseName": {
|
||||
"contractScope": null,
|
||||
"id": 585,
|
||||
"id": 609,
|
||||
"name": "SelfAuthorized",
|
||||
"nodeType": "UserDefinedTypeName",
|
||||
"referencedDeclaration": 1449,
|
||||
"src": "226:14:4",
|
||||
"referencedDeclaration": 3065,
|
||||
"src": "226:14:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_contract$_SelfAuthorized_$1449",
|
||||
"typeIdentifier": "t_contract$_SelfAuthorized_$3065",
|
||||
"typeString": "contract SelfAuthorized"
|
||||
}
|
||||
},
|
||||
"id": 586,
|
||||
"id": 610,
|
||||
"nodeType": "InheritanceSpecifier",
|
||||
"src": "226:14:4"
|
||||
"src": "226:14:5"
|
||||
}
|
||||
],
|
||||
"contractDependencies": [
|
||||
1449
|
||||
3065
|
||||
],
|
||||
"contractKind": "contract",
|
||||
"documentation": "@title MasterCopy - Base for master copy contracts (should always be first super contract)\n @author Richard Meissner - <richard@gnosis.pm>",
|
||||
"fullyImplemented": true,
|
||||
"id": 608,
|
||||
"id": 632,
|
||||
"linearizedBaseContracts": [
|
||||
608,
|
||||
1449
|
||||
632,
|
||||
3065
|
||||
],
|
||||
"name": "MasterCopy",
|
||||
"nodeType": "ContractDefinition",
|
||||
"nodes": [
|
||||
{
|
||||
"constant": false,
|
||||
"id": 588,
|
||||
"id": 612,
|
||||
"name": "masterCopy",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"scope": 608,
|
||||
"src": "465:18:4",
|
||||
"scope": 632,
|
||||
"src": "465:18:5",
|
||||
"stateVariable": true,
|
||||
"storageLocation": "default",
|
||||
"typeDescriptions": {
|
||||
|
@ -102,10 +102,10 @@
|
|||
"typeString": "address"
|
||||
},
|
||||
"typeName": {
|
||||
"id": 587,
|
||||
"id": 611,
|
||||
"name": "address",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "465:7:4",
|
||||
"src": "465:7:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
|
@ -116,9 +116,9 @@
|
|||
},
|
||||
{
|
||||
"body": {
|
||||
"id": 606,
|
||||
"id": 630,
|
||||
"nodeType": "Block",
|
||||
"src": "711:163:4",
|
||||
"src": "711:163:5",
|
||||
"statements": [
|
||||
{
|
||||
"expression": {
|
||||
|
@ -130,19 +130,19 @@
|
|||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
},
|
||||
"id": 598,
|
||||
"id": 622,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": false,
|
||||
"lValueRequested": false,
|
||||
"leftExpression": {
|
||||
"argumentTypes": null,
|
||||
"id": 596,
|
||||
"id": 620,
|
||||
"name": "_masterCopy",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 590,
|
||||
"src": "776:11:4",
|
||||
"referencedDeclaration": 614,
|
||||
"src": "776:11:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
|
@ -153,14 +153,14 @@
|
|||
"rightExpression": {
|
||||
"argumentTypes": null,
|
||||
"hexValue": "30",
|
||||
"id": 597,
|
||||
"id": 621,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": true,
|
||||
"kind": "number",
|
||||
"lValueRequested": false,
|
||||
"nodeType": "Literal",
|
||||
"src": "791:1:4",
|
||||
"src": "791:1:5",
|
||||
"subdenomination": null,
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_rational_0_by_1",
|
||||
|
@ -168,7 +168,7 @@
|
|||
},
|
||||
"value": "0"
|
||||
},
|
||||
"src": "776:16:4",
|
||||
"src": "776:16:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_bool",
|
||||
"typeString": "bool"
|
||||
|
@ -177,14 +177,14 @@
|
|||
{
|
||||
"argumentTypes": null,
|
||||
"hexValue": "496e76616c6964206d617374657220636f707920616464726573732070726f7669646564",
|
||||
"id": 599,
|
||||
"id": 623,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": true,
|
||||
"kind": "string",
|
||||
"lValueRequested": false,
|
||||
"nodeType": "Literal",
|
||||
"src": "794:38:4",
|
||||
"src": "794:38:5",
|
||||
"subdenomination": null,
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_stringliteral_108d84599042957b954e89d43b52f80be89321dfc114a37800028eba58dafc87",
|
||||
|
@ -204,21 +204,21 @@
|
|||
"typeString": "literal_string \"Invalid master copy address provided\""
|
||||
}
|
||||
],
|
||||
"id": 595,
|
||||
"id": 619,
|
||||
"name": "require",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [
|
||||
1776,
|
||||
1777
|
||||
4039,
|
||||
4040
|
||||
],
|
||||
"referencedDeclaration": 1777,
|
||||
"src": "768:7:4",
|
||||
"referencedDeclaration": 4040,
|
||||
"src": "768:7:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$",
|
||||
"typeString": "function (bool,string memory) pure"
|
||||
}
|
||||
},
|
||||
"id": 600,
|
||||
"id": 624,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": false,
|
||||
|
@ -226,32 +226,32 @@
|
|||
"lValueRequested": false,
|
||||
"names": [],
|
||||
"nodeType": "FunctionCall",
|
||||
"src": "768:65:4",
|
||||
"src": "768:65:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_tuple$__$",
|
||||
"typeString": "tuple()"
|
||||
}
|
||||
},
|
||||
"id": 601,
|
||||
"id": 625,
|
||||
"nodeType": "ExpressionStatement",
|
||||
"src": "768:65:4"
|
||||
"src": "768:65:5"
|
||||
},
|
||||
{
|
||||
"expression": {
|
||||
"argumentTypes": null,
|
||||
"id": 604,
|
||||
"id": 628,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": false,
|
||||
"lValueRequested": false,
|
||||
"leftHandSide": {
|
||||
"argumentTypes": null,
|
||||
"id": 602,
|
||||
"id": 626,
|
||||
"name": "masterCopy",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 588,
|
||||
"src": "843:10:4",
|
||||
"referencedDeclaration": 612,
|
||||
"src": "843:10:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
|
@ -261,68 +261,68 @@
|
|||
"operator": "=",
|
||||
"rightHandSide": {
|
||||
"argumentTypes": null,
|
||||
"id": 603,
|
||||
"id": 627,
|
||||
"name": "_masterCopy",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 590,
|
||||
"src": "856:11:4",
|
||||
"referencedDeclaration": 614,
|
||||
"src": "856:11:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
}
|
||||
},
|
||||
"src": "843:24:4",
|
||||
"src": "843:24:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
}
|
||||
},
|
||||
"id": 605,
|
||||
"id": 629,
|
||||
"nodeType": "ExpressionStatement",
|
||||
"src": "843:24:4"
|
||||
"src": "843:24:5"
|
||||
}
|
||||
]
|
||||
},
|
||||
"documentation": "@dev Allows to upgrade the contract. This can only be done via a Safe transaction.\n @param _masterCopy New contract address.",
|
||||
"id": 607,
|
||||
"id": 631,
|
||||
"implemented": true,
|
||||
"isConstructor": false,
|
||||
"isDeclaredConst": false,
|
||||
"modifiers": [
|
||||
{
|
||||
"arguments": null,
|
||||
"id": 593,
|
||||
"id": 617,
|
||||
"modifierName": {
|
||||
"argumentTypes": null,
|
||||
"id": 592,
|
||||
"id": 616,
|
||||
"name": "authorized",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 1448,
|
||||
"src": "696:10:4",
|
||||
"referencedDeclaration": 3064,
|
||||
"src": "696:10:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_modifier$__$",
|
||||
"typeString": "modifier ()"
|
||||
}
|
||||
},
|
||||
"nodeType": "ModifierInvocation",
|
||||
"src": "696:10:4"
|
||||
"src": "696:10:5"
|
||||
}
|
||||
],
|
||||
"name": "changeMasterCopy",
|
||||
"nodeType": "FunctionDefinition",
|
||||
"parameters": {
|
||||
"id": 591,
|
||||
"id": 615,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [
|
||||
{
|
||||
"constant": false,
|
||||
"id": 590,
|
||||
"id": 614,
|
||||
"name": "_masterCopy",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"scope": 607,
|
||||
"src": "652:19:4",
|
||||
"scope": 631,
|
||||
"src": "652:19:5",
|
||||
"stateVariable": false,
|
||||
"storageLocation": "default",
|
||||
"typeDescriptions": {
|
||||
|
@ -330,10 +330,10 @@
|
|||
"typeString": "address"
|
||||
},
|
||||
"typeName": {
|
||||
"id": 589,
|
||||
"id": 613,
|
||||
"name": "address",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "652:7:4",
|
||||
"src": "652:7:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
|
@ -343,56 +343,56 @@
|
|||
"visibility": "internal"
|
||||
}
|
||||
],
|
||||
"src": "651:21:4"
|
||||
"src": "651:21:5"
|
||||
},
|
||||
"payable": false,
|
||||
"returnParameters": {
|
||||
"id": 594,
|
||||
"id": 618,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [],
|
||||
"src": "711:0:4"
|
||||
"src": "711:0:5"
|
||||
},
|
||||
"scope": 608,
|
||||
"src": "626:248:4",
|
||||
"scope": 632,
|
||||
"src": "626:248:5",
|
||||
"stateMutability": "nonpayable",
|
||||
"superFunction": null,
|
||||
"visibility": "public"
|
||||
}
|
||||
],
|
||||
"scope": 609,
|
||||
"src": "203:673:4"
|
||||
"scope": 633,
|
||||
"src": "203:673:5"
|
||||
}
|
||||
],
|
||||
"src": "0:877:4"
|
||||
"src": "0:877:5"
|
||||
},
|
||||
"legacyAST": {
|
||||
"absolutePath": "/Users/apanizo/git/gnosis/safe-contracts/contracts/MasterCopy.sol",
|
||||
"exportedSymbols": {
|
||||
"MasterCopy": [
|
||||
608
|
||||
632
|
||||
]
|
||||
},
|
||||
"id": 609,
|
||||
"id": 633,
|
||||
"nodeType": "SourceUnit",
|
||||
"nodes": [
|
||||
{
|
||||
"id": 583,
|
||||
"id": 607,
|
||||
"literals": [
|
||||
"solidity",
|
||||
"0.4",
|
||||
".24"
|
||||
],
|
||||
"nodeType": "PragmaDirective",
|
||||
"src": "0:23:4"
|
||||
"src": "0:23:5"
|
||||
},
|
||||
{
|
||||
"absolutePath": "/Users/apanizo/git/gnosis/safe-contracts/contracts/SelfAuthorized.sol",
|
||||
"file": "./SelfAuthorized.sol",
|
||||
"id": 584,
|
||||
"id": 608,
|
||||
"nodeType": "ImportDirective",
|
||||
"scope": 609,
|
||||
"sourceUnit": 1450,
|
||||
"src": "24:30:4",
|
||||
"scope": 633,
|
||||
"sourceUnit": 3066,
|
||||
"src": "24:30:5",
|
||||
"symbolAliases": [],
|
||||
"unitAlias": ""
|
||||
},
|
||||
|
@ -402,42 +402,42 @@
|
|||
"arguments": null,
|
||||
"baseName": {
|
||||
"contractScope": null,
|
||||
"id": 585,
|
||||
"id": 609,
|
||||
"name": "SelfAuthorized",
|
||||
"nodeType": "UserDefinedTypeName",
|
||||
"referencedDeclaration": 1449,
|
||||
"src": "226:14:4",
|
||||
"referencedDeclaration": 3065,
|
||||
"src": "226:14:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_contract$_SelfAuthorized_$1449",
|
||||
"typeIdentifier": "t_contract$_SelfAuthorized_$3065",
|
||||
"typeString": "contract SelfAuthorized"
|
||||
}
|
||||
},
|
||||
"id": 586,
|
||||
"id": 610,
|
||||
"nodeType": "InheritanceSpecifier",
|
||||
"src": "226:14:4"
|
||||
"src": "226:14:5"
|
||||
}
|
||||
],
|
||||
"contractDependencies": [
|
||||
1449
|
||||
3065
|
||||
],
|
||||
"contractKind": "contract",
|
||||
"documentation": "@title MasterCopy - Base for master copy contracts (should always be first super contract)\n @author Richard Meissner - <richard@gnosis.pm>",
|
||||
"fullyImplemented": true,
|
||||
"id": 608,
|
||||
"id": 632,
|
||||
"linearizedBaseContracts": [
|
||||
608,
|
||||
1449
|
||||
632,
|
||||
3065
|
||||
],
|
||||
"name": "MasterCopy",
|
||||
"nodeType": "ContractDefinition",
|
||||
"nodes": [
|
||||
{
|
||||
"constant": false,
|
||||
"id": 588,
|
||||
"id": 612,
|
||||
"name": "masterCopy",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"scope": 608,
|
||||
"src": "465:18:4",
|
||||
"scope": 632,
|
||||
"src": "465:18:5",
|
||||
"stateVariable": true,
|
||||
"storageLocation": "default",
|
||||
"typeDescriptions": {
|
||||
|
@ -445,10 +445,10 @@
|
|||
"typeString": "address"
|
||||
},
|
||||
"typeName": {
|
||||
"id": 587,
|
||||
"id": 611,
|
||||
"name": "address",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "465:7:4",
|
||||
"src": "465:7:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
|
@ -459,9 +459,9 @@
|
|||
},
|
||||
{
|
||||
"body": {
|
||||
"id": 606,
|
||||
"id": 630,
|
||||
"nodeType": "Block",
|
||||
"src": "711:163:4",
|
||||
"src": "711:163:5",
|
||||
"statements": [
|
||||
{
|
||||
"expression": {
|
||||
|
@ -473,19 +473,19 @@
|
|||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
},
|
||||
"id": 598,
|
||||
"id": 622,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": false,
|
||||
"lValueRequested": false,
|
||||
"leftExpression": {
|
||||
"argumentTypes": null,
|
||||
"id": 596,
|
||||
"id": 620,
|
||||
"name": "_masterCopy",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 590,
|
||||
"src": "776:11:4",
|
||||
"referencedDeclaration": 614,
|
||||
"src": "776:11:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
|
@ -496,14 +496,14 @@
|
|||
"rightExpression": {
|
||||
"argumentTypes": null,
|
||||
"hexValue": "30",
|
||||
"id": 597,
|
||||
"id": 621,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": true,
|
||||
"kind": "number",
|
||||
"lValueRequested": false,
|
||||
"nodeType": "Literal",
|
||||
"src": "791:1:4",
|
||||
"src": "791:1:5",
|
||||
"subdenomination": null,
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_rational_0_by_1",
|
||||
|
@ -511,7 +511,7 @@
|
|||
},
|
||||
"value": "0"
|
||||
},
|
||||
"src": "776:16:4",
|
||||
"src": "776:16:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_bool",
|
||||
"typeString": "bool"
|
||||
|
@ -520,14 +520,14 @@
|
|||
{
|
||||
"argumentTypes": null,
|
||||
"hexValue": "496e76616c6964206d617374657220636f707920616464726573732070726f7669646564",
|
||||
"id": 599,
|
||||
"id": 623,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": true,
|
||||
"kind": "string",
|
||||
"lValueRequested": false,
|
||||
"nodeType": "Literal",
|
||||
"src": "794:38:4",
|
||||
"src": "794:38:5",
|
||||
"subdenomination": null,
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_stringliteral_108d84599042957b954e89d43b52f80be89321dfc114a37800028eba58dafc87",
|
||||
|
@ -547,21 +547,21 @@
|
|||
"typeString": "literal_string \"Invalid master copy address provided\""
|
||||
}
|
||||
],
|
||||
"id": 595,
|
||||
"id": 619,
|
||||
"name": "require",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [
|
||||
1776,
|
||||
1777
|
||||
4039,
|
||||
4040
|
||||
],
|
||||
"referencedDeclaration": 1777,
|
||||
"src": "768:7:4",
|
||||
"referencedDeclaration": 4040,
|
||||
"src": "768:7:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$",
|
||||
"typeString": "function (bool,string memory) pure"
|
||||
}
|
||||
},
|
||||
"id": 600,
|
||||
"id": 624,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": false,
|
||||
|
@ -569,32 +569,32 @@
|
|||
"lValueRequested": false,
|
||||
"names": [],
|
||||
"nodeType": "FunctionCall",
|
||||
"src": "768:65:4",
|
||||
"src": "768:65:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_tuple$__$",
|
||||
"typeString": "tuple()"
|
||||
}
|
||||
},
|
||||
"id": 601,
|
||||
"id": 625,
|
||||
"nodeType": "ExpressionStatement",
|
||||
"src": "768:65:4"
|
||||
"src": "768:65:5"
|
||||
},
|
||||
{
|
||||
"expression": {
|
||||
"argumentTypes": null,
|
||||
"id": 604,
|
||||
"id": 628,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": false,
|
||||
"lValueRequested": false,
|
||||
"leftHandSide": {
|
||||
"argumentTypes": null,
|
||||
"id": 602,
|
||||
"id": 626,
|
||||
"name": "masterCopy",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 588,
|
||||
"src": "843:10:4",
|
||||
"referencedDeclaration": 612,
|
||||
"src": "843:10:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
|
@ -604,68 +604,68 @@
|
|||
"operator": "=",
|
||||
"rightHandSide": {
|
||||
"argumentTypes": null,
|
||||
"id": 603,
|
||||
"id": 627,
|
||||
"name": "_masterCopy",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 590,
|
||||
"src": "856:11:4",
|
||||
"referencedDeclaration": 614,
|
||||
"src": "856:11:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
}
|
||||
},
|
||||
"src": "843:24:4",
|
||||
"src": "843:24:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
}
|
||||
},
|
||||
"id": 605,
|
||||
"id": 629,
|
||||
"nodeType": "ExpressionStatement",
|
||||
"src": "843:24:4"
|
||||
"src": "843:24:5"
|
||||
}
|
||||
]
|
||||
},
|
||||
"documentation": "@dev Allows to upgrade the contract. This can only be done via a Safe transaction.\n @param _masterCopy New contract address.",
|
||||
"id": 607,
|
||||
"id": 631,
|
||||
"implemented": true,
|
||||
"isConstructor": false,
|
||||
"isDeclaredConst": false,
|
||||
"modifiers": [
|
||||
{
|
||||
"arguments": null,
|
||||
"id": 593,
|
||||
"id": 617,
|
||||
"modifierName": {
|
||||
"argumentTypes": null,
|
||||
"id": 592,
|
||||
"id": 616,
|
||||
"name": "authorized",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 1448,
|
||||
"src": "696:10:4",
|
||||
"referencedDeclaration": 3064,
|
||||
"src": "696:10:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_modifier$__$",
|
||||
"typeString": "modifier ()"
|
||||
}
|
||||
},
|
||||
"nodeType": "ModifierInvocation",
|
||||
"src": "696:10:4"
|
||||
"src": "696:10:5"
|
||||
}
|
||||
],
|
||||
"name": "changeMasterCopy",
|
||||
"nodeType": "FunctionDefinition",
|
||||
"parameters": {
|
||||
"id": 591,
|
||||
"id": 615,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [
|
||||
{
|
||||
"constant": false,
|
||||
"id": 590,
|
||||
"id": 614,
|
||||
"name": "_masterCopy",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"scope": 607,
|
||||
"src": "652:19:4",
|
||||
"scope": 631,
|
||||
"src": "652:19:5",
|
||||
"stateVariable": false,
|
||||
"storageLocation": "default",
|
||||
"typeDescriptions": {
|
||||
|
@ -673,10 +673,10 @@
|
|||
"typeString": "address"
|
||||
},
|
||||
"typeName": {
|
||||
"id": 589,
|
||||
"id": 613,
|
||||
"name": "address",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "652:7:4",
|
||||
"src": "652:7:5",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
|
@ -686,27 +686,27 @@
|
|||
"visibility": "internal"
|
||||
}
|
||||
],
|
||||
"src": "651:21:4"
|
||||
"src": "651:21:5"
|
||||
},
|
||||
"payable": false,
|
||||
"returnParameters": {
|
||||
"id": 594,
|
||||
"id": 618,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [],
|
||||
"src": "711:0:4"
|
||||
"src": "711:0:5"
|
||||
},
|
||||
"scope": 608,
|
||||
"src": "626:248:4",
|
||||
"scope": 632,
|
||||
"src": "626:248:5",
|
||||
"stateMutability": "nonpayable",
|
||||
"superFunction": null,
|
||||
"visibility": "public"
|
||||
}
|
||||
],
|
||||
"scope": 609,
|
||||
"src": "203:673:4"
|
||||
"scope": 633,
|
||||
"src": "203:673:5"
|
||||
}
|
||||
],
|
||||
"src": "0:877:4"
|
||||
"src": "0:877:5"
|
||||
},
|
||||
"compiler": {
|
||||
"name": "solc",
|
||||
|
@ -714,5 +714,5 @@
|
|||
},
|
||||
"networks": {},
|
||||
"schemaVersion": "2.0.0",
|
||||
"updatedAt": "2018-06-20T07:57:27.004Z"
|
||||
"updatedAt": "2018-06-29T09:01:22.057Z"
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -18,29 +18,29 @@
|
|||
],
|
||||
"bytecode": "0x608060405234801561001057600080fd5b50610169806100206000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680638d80ff0a14610046575b600080fd5b34801561005257600080fd5b506100ad600480360381019080803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506100af565b005b805160205b81811015610138578083015160208201840151604083018501516080840186015160a085018701600085600081146100f357600181146101035761010e565b6000808585888a5af1915061010e565b6000808585895af491505b50600081141561011d57600080fd5b602080601f8501040260a001870196505050505050506100b4565b5050505600a165627a7a72305820c1abf4988401674d6d6a5ceca56de123817c61305360ceffe26ebab59a828b7d0029",
|
||||
"deployedBytecode": "0x608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680638d80ff0a14610046575b600080fd5b34801561005257600080fd5b506100ad600480360381019080803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506100af565b005b805160205b81811015610138578083015160208201840151604083018501516080840186015160a085018701600085600081146100f357600181146101035761010e565b6000808585888a5af1915061010e565b6000808585895af491505b50600081141561011d57600080fd5b602080601f8501040260a001870196505050505050506100b4565b5050505600a165627a7a72305820c1abf4988401674d6d6a5ceca56de123817c61305360ceffe26ebab59a828b7d0029",
|
||||
"sourceMap": "253:1424:17:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;253:1424:17;;;;;;;",
|
||||
"deployedSourceMap": "253:1424:17:-;;;;;;;;;;;;;;;;;;;;;;;;695:980;;8:9:-1;5:2;;;30:1;27;20:12;5:2;695:980:17;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;870:12;864:19;905:4;922:737;936:6;933:1;930:13;922:737;;;1007:1;993:12;989:20;983:27;1068:4;1065:1;1061:12;1047;1043:31;1037:38;1136:4;1133:1;1129:12;1115;1111:31;1105:38;1209:4;1206:1;1202:12;1188;1184:31;1178:38;1270:4;1267:1;1263:12;1249;1245:31;1308:1;1333:9;1365:1;1360:66;;;;1448:1;1443:67;;;;1326:184;;1360:66;1422:1;1419;1407:10;1401:4;1394:5;1390:2;1385:3;1380:44;1369:55;;1360:66;;1443:67;1506:1;1503;1491:10;1485:4;1481:2;1476:3;1463:45;1452:56;;1326:184;;1542:1;1533:7;1530:14;1527:2;;;1557:1;1554;1547:12;1527:2;1638:4;1631;1624;1612:10;1608:21;1604:32;1600:43;1594:4;1590:54;1587:1;1583:62;1578:67;;948:711;;;;;;922:737;;;836:833;;;:::o",
|
||||
"sourceMap": "253:1424:19:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;253:1424:19;;;;;;;",
|
||||
"deployedSourceMap": "253:1424:19:-;;;;;;;;;;;;;;;;;;;;;;;;695:980;;8:9:-1;5:2;;;30:1;27;20:12;5:2;695:980:19;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;870:12;864:19;905:4;922:737;936:6;933:1;930:13;922:737;;;1007:1;993:12;989:20;983:27;1068:4;1065:1;1061:12;1047;1043:31;1037:38;1136:4;1133:1;1129:12;1115;1111:31;1105:38;1209:4;1206:1;1202:12;1188;1184:31;1178:38;1270:4;1267:1;1263:12;1249;1245:31;1308:1;1333:9;1365:1;1360:66;;;;1448:1;1443:67;;;;1326:184;;1360:66;1422:1;1419;1407:10;1401:4;1394:5;1390:2;1385:3;1380:44;1369:55;;1360:66;;1443:67;1506:1;1503;1491:10;1485:4;1481:2;1476:3;1463:45;1452:56;;1326:184;;1542:1;1533:7;1530:14;1527:2;;;1557:1;1554;1547:12;1527:2;1638:4;1631;1624;1612:10;1608:21;1604:32;1600:43;1594:4;1590:54;1587:1;1583:62;1578:67;;948:711;;;;;;922:737;;;836:833;;;:::o",
|
||||
"source": "pragma solidity 0.4.24;\n\n\n/// @title Multi Send - Allows to batch multiple transactions into one.\n/// @author Nick Dodson - <nick.dodson@consensys.net>\n/// @author Gonçalo Sá - <goncalo.sa@consensys.net>\n/// @author Stefan George - <stefan@gnosis.pm>\ncontract MultiSend {\n\n /// @dev Sends multiple transactions and reverts all if one fails.\n /// @param transactions Encoded transactions. Each transaction is encoded as a \n /// tuple(operation,address,uint256,bytes), where operation \n /// can be 0 for a call or 1 for a delegatecall. The bytes \n /// of all encoded transactions are concatenated to form the input.\n function multiSend(bytes transactions)\n public\n {\n // solium-disable-next-line security/no-inline-assembly\n assembly {\n let length := mload(transactions)\n let i := 0x20\n for { } lt(i, length) { } {\n let operation := mload(add(transactions, i))\n let to := mload(add(transactions, add(i, 0x20)))\n let value := mload(add(transactions, add(i, 0x40)))\n let dataLength := mload(add(transactions, add(i, 0x80)))\n let data := add(transactions, add(i, 0xa0))\n let success := 0\n switch operation \n case 0 { success := call(gas, to, value, data, dataLength, 0, 0) }\n case 1 { success := delegatecall(gas, to, data, dataLength, 0, 0) }\n if eq(success, 0) { revert(0, 0) }\n i := add(i, add(0xa0, mul(div(add(dataLength, 0x1f), 0x20), 0x20)))\n }\n }\n }\n}\n",
|
||||
"sourcePath": "/Users/apanizo/git/gnosis/safe-contracts/contracts/libraries/MultiSend.sol",
|
||||
"ast": {
|
||||
"absolutePath": "/Users/apanizo/git/gnosis/safe-contracts/contracts/libraries/MultiSend.sol",
|
||||
"exportedSymbols": {
|
||||
"MultiSend": [
|
||||
1840
|
||||
3176
|
||||
]
|
||||
},
|
||||
"id": 1841,
|
||||
"id": 3177,
|
||||
"nodeType": "SourceUnit",
|
||||
"nodes": [
|
||||
{
|
||||
"id": 1832,
|
||||
"id": 3168,
|
||||
"literals": [
|
||||
"solidity",
|
||||
"0.4",
|
||||
".24"
|
||||
],
|
||||
"nodeType": "PragmaDirective",
|
||||
"src": "0:23:17"
|
||||
"src": "0:23:19"
|
||||
},
|
||||
{
|
||||
"baseContracts": [],
|
||||
|
@ -48,85 +48,85 @@
|
|||
"contractKind": "contract",
|
||||
"documentation": "@title Multi Send - Allows to batch multiple transactions into one.\n @author Nick Dodson - <nick.dodson@consensys.net>\n @author Gonçalo Sá - <goncalo.sa@consensys.net>\n @author Stefan George - <stefan@gnosis.pm>",
|
||||
"fullyImplemented": true,
|
||||
"id": 1840,
|
||||
"id": 3176,
|
||||
"linearizedBaseContracts": [
|
||||
1840
|
||||
3176
|
||||
],
|
||||
"name": "MultiSend",
|
||||
"nodeType": "ContractDefinition",
|
||||
"nodes": [
|
||||
{
|
||||
"body": {
|
||||
"id": 1838,
|
||||
"id": 3174,
|
||||
"nodeType": "Block",
|
||||
"src": "753:922:17",
|
||||
"src": "753:922:19",
|
||||
"statements": [
|
||||
{
|
||||
"externalReferences": [
|
||||
{
|
||||
"transactions": {
|
||||
"declaration": 1834,
|
||||
"declaration": 3170,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "870:12:17",
|
||||
"src": "870:12:19",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"transactions": {
|
||||
"declaration": 1834,
|
||||
"declaration": 3170,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "993:12:17",
|
||||
"src": "993:12:19",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"transactions": {
|
||||
"declaration": 1834,
|
||||
"declaration": 3170,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "1047:12:17",
|
||||
"src": "1047:12:19",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"transactions": {
|
||||
"declaration": 1834,
|
||||
"declaration": 3170,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "1188:12:17",
|
||||
"src": "1188:12:19",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"transactions": {
|
||||
"declaration": 1834,
|
||||
"declaration": 3170,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "1115:12:17",
|
||||
"src": "1115:12:19",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"transactions": {
|
||||
"declaration": 1834,
|
||||
"declaration": 3170,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "1249:12:17",
|
||||
"src": "1249:12:19",
|
||||
"valueSize": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": 1837,
|
||||
"id": 3173,
|
||||
"nodeType": "InlineAssembly",
|
||||
"operations": "{\n let length := mload(transactions)\n let i := 0x20\n for {\n }\n lt(i, length)\n {\n }\n {\n let operation := mload(add(transactions, i))\n let to := mload(add(transactions, add(i, 0x20)))\n let value := mload(add(transactions, add(i, 0x40)))\n let dataLength := mload(add(transactions, add(i, 0x80)))\n let data := add(transactions, add(i, 0xa0))\n let success := 0\n switch operation\n case 0 {\n success := call(gas(), to, value, data, dataLength, 0, 0)\n }\n case 1 {\n success := delegatecall(gas(), to, data, dataLength, 0, 0)\n }\n if eq(success, 0)\n {\n revert(0, 0)\n }\n i := add(i, add(0xa0, mul(div(add(dataLength, 0x1f), 0x20), 0x20)))\n }\n}",
|
||||
"src": "827:848:17"
|
||||
"src": "827:848:19"
|
||||
}
|
||||
]
|
||||
},
|
||||
"documentation": "@dev Sends multiple transactions and reverts all if one fails.\n @param transactions Encoded transactions. Each transaction is encoded as a \n tuple(operation,address,uint256,bytes), where operation \n can be 0 for a call or 1 for a delegatecall. The bytes \n of all encoded transactions are concatenated to form the input.",
|
||||
"id": 1839,
|
||||
"id": 3175,
|
||||
"implemented": true,
|
||||
"isConstructor": false,
|
||||
"isDeclaredConst": false,
|
||||
|
@ -134,16 +134,16 @@
|
|||
"name": "multiSend",
|
||||
"nodeType": "FunctionDefinition",
|
||||
"parameters": {
|
||||
"id": 1835,
|
||||
"id": 3171,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [
|
||||
{
|
||||
"constant": false,
|
||||
"id": 1834,
|
||||
"id": 3170,
|
||||
"name": "transactions",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"scope": 1839,
|
||||
"src": "714:18:17",
|
||||
"scope": 3175,
|
||||
"src": "714:18:19",
|
||||
"stateVariable": false,
|
||||
"storageLocation": "default",
|
||||
"typeDescriptions": {
|
||||
|
@ -151,10 +151,10 @@
|
|||
"typeString": "bytes"
|
||||
},
|
||||
"typeName": {
|
||||
"id": 1833,
|
||||
"id": 3169,
|
||||
"name": "bytes",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "714:5:17",
|
||||
"src": "714:5:19",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_bytes_storage_ptr",
|
||||
"typeString": "bytes"
|
||||
|
@ -164,47 +164,47 @@
|
|||
"visibility": "internal"
|
||||
}
|
||||
],
|
||||
"src": "713:20:17"
|
||||
"src": "713:20:19"
|
||||
},
|
||||
"payable": false,
|
||||
"returnParameters": {
|
||||
"id": 1836,
|
||||
"id": 3172,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [],
|
||||
"src": "753:0:17"
|
||||
"src": "753:0:19"
|
||||
},
|
||||
"scope": 1840,
|
||||
"src": "695:980:17",
|
||||
"scope": 3176,
|
||||
"src": "695:980:19",
|
||||
"stateMutability": "nonpayable",
|
||||
"superFunction": null,
|
||||
"visibility": "public"
|
||||
}
|
||||
],
|
||||
"scope": 1841,
|
||||
"src": "253:1424:17"
|
||||
"scope": 3177,
|
||||
"src": "253:1424:19"
|
||||
}
|
||||
],
|
||||
"src": "0:1678:17"
|
||||
"src": "0:1678:19"
|
||||
},
|
||||
"legacyAST": {
|
||||
"absolutePath": "/Users/apanizo/git/gnosis/safe-contracts/contracts/libraries/MultiSend.sol",
|
||||
"exportedSymbols": {
|
||||
"MultiSend": [
|
||||
1840
|
||||
3176
|
||||
]
|
||||
},
|
||||
"id": 1841,
|
||||
"id": 3177,
|
||||
"nodeType": "SourceUnit",
|
||||
"nodes": [
|
||||
{
|
||||
"id": 1832,
|
||||
"id": 3168,
|
||||
"literals": [
|
||||
"solidity",
|
||||
"0.4",
|
||||
".24"
|
||||
],
|
||||
"nodeType": "PragmaDirective",
|
||||
"src": "0:23:17"
|
||||
"src": "0:23:19"
|
||||
},
|
||||
{
|
||||
"baseContracts": [],
|
||||
|
@ -212,85 +212,85 @@
|
|||
"contractKind": "contract",
|
||||
"documentation": "@title Multi Send - Allows to batch multiple transactions into one.\n @author Nick Dodson - <nick.dodson@consensys.net>\n @author Gonçalo Sá - <goncalo.sa@consensys.net>\n @author Stefan George - <stefan@gnosis.pm>",
|
||||
"fullyImplemented": true,
|
||||
"id": 1840,
|
||||
"id": 3176,
|
||||
"linearizedBaseContracts": [
|
||||
1840
|
||||
3176
|
||||
],
|
||||
"name": "MultiSend",
|
||||
"nodeType": "ContractDefinition",
|
||||
"nodes": [
|
||||
{
|
||||
"body": {
|
||||
"id": 1838,
|
||||
"id": 3174,
|
||||
"nodeType": "Block",
|
||||
"src": "753:922:17",
|
||||
"src": "753:922:19",
|
||||
"statements": [
|
||||
{
|
||||
"externalReferences": [
|
||||
{
|
||||
"transactions": {
|
||||
"declaration": 1834,
|
||||
"declaration": 3170,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "870:12:17",
|
||||
"src": "870:12:19",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"transactions": {
|
||||
"declaration": 1834,
|
||||
"declaration": 3170,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "993:12:17",
|
||||
"src": "993:12:19",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"transactions": {
|
||||
"declaration": 1834,
|
||||
"declaration": 3170,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "1047:12:17",
|
||||
"src": "1047:12:19",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"transactions": {
|
||||
"declaration": 1834,
|
||||
"declaration": 3170,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "1188:12:17",
|
||||
"src": "1188:12:19",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"transactions": {
|
||||
"declaration": 1834,
|
||||
"declaration": 3170,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "1115:12:17",
|
||||
"src": "1115:12:19",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"transactions": {
|
||||
"declaration": 1834,
|
||||
"declaration": 3170,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "1249:12:17",
|
||||
"src": "1249:12:19",
|
||||
"valueSize": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": 1837,
|
||||
"id": 3173,
|
||||
"nodeType": "InlineAssembly",
|
||||
"operations": "{\n let length := mload(transactions)\n let i := 0x20\n for {\n }\n lt(i, length)\n {\n }\n {\n let operation := mload(add(transactions, i))\n let to := mload(add(transactions, add(i, 0x20)))\n let value := mload(add(transactions, add(i, 0x40)))\n let dataLength := mload(add(transactions, add(i, 0x80)))\n let data := add(transactions, add(i, 0xa0))\n let success := 0\n switch operation\n case 0 {\n success := call(gas(), to, value, data, dataLength, 0, 0)\n }\n case 1 {\n success := delegatecall(gas(), to, data, dataLength, 0, 0)\n }\n if eq(success, 0)\n {\n revert(0, 0)\n }\n i := add(i, add(0xa0, mul(div(add(dataLength, 0x1f), 0x20), 0x20)))\n }\n}",
|
||||
"src": "827:848:17"
|
||||
"src": "827:848:19"
|
||||
}
|
||||
]
|
||||
},
|
||||
"documentation": "@dev Sends multiple transactions and reverts all if one fails.\n @param transactions Encoded transactions. Each transaction is encoded as a \n tuple(operation,address,uint256,bytes), where operation \n can be 0 for a call or 1 for a delegatecall. The bytes \n of all encoded transactions are concatenated to form the input.",
|
||||
"id": 1839,
|
||||
"id": 3175,
|
||||
"implemented": true,
|
||||
"isConstructor": false,
|
||||
"isDeclaredConst": false,
|
||||
|
@ -298,16 +298,16 @@
|
|||
"name": "multiSend",
|
||||
"nodeType": "FunctionDefinition",
|
||||
"parameters": {
|
||||
"id": 1835,
|
||||
"id": 3171,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [
|
||||
{
|
||||
"constant": false,
|
||||
"id": 1834,
|
||||
"id": 3170,
|
||||
"name": "transactions",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"scope": 1839,
|
||||
"src": "714:18:17",
|
||||
"scope": 3175,
|
||||
"src": "714:18:19",
|
||||
"stateVariable": false,
|
||||
"storageLocation": "default",
|
||||
"typeDescriptions": {
|
||||
|
@ -315,10 +315,10 @@
|
|||
"typeString": "bytes"
|
||||
},
|
||||
"typeName": {
|
||||
"id": 1833,
|
||||
"id": 3169,
|
||||
"name": "bytes",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "714:5:17",
|
||||
"src": "714:5:19",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_bytes_storage_ptr",
|
||||
"typeString": "bytes"
|
||||
|
@ -328,27 +328,27 @@
|
|||
"visibility": "internal"
|
||||
}
|
||||
],
|
||||
"src": "713:20:17"
|
||||
"src": "713:20:19"
|
||||
},
|
||||
"payable": false,
|
||||
"returnParameters": {
|
||||
"id": 1836,
|
||||
"id": 3172,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [],
|
||||
"src": "753:0:17"
|
||||
"src": "753:0:19"
|
||||
},
|
||||
"scope": 1840,
|
||||
"src": "695:980:17",
|
||||
"scope": 3176,
|
||||
"src": "695:980:19",
|
||||
"stateMutability": "nonpayable",
|
||||
"superFunction": null,
|
||||
"visibility": "public"
|
||||
}
|
||||
],
|
||||
"scope": 1841,
|
||||
"src": "253:1424:17"
|
||||
"scope": 3177,
|
||||
"src": "253:1424:19"
|
||||
}
|
||||
],
|
||||
"src": "0:1678:17"
|
||||
"src": "0:1678:19"
|
||||
},
|
||||
"compiler": {
|
||||
"name": "solc",
|
||||
|
@ -358,28 +358,28 @@
|
|||
"4": {
|
||||
"events": {},
|
||||
"links": {},
|
||||
"address": "0xe94c33a523bf201e412f601eff906eec0b1d21f5",
|
||||
"transactionHash": "0x1f04811d6fb97329f6e96a579587fe5a3abe9fe2f202d4b2e27adb78d3de1c48"
|
||||
"address": "0x7115893bc1477bc22f127bc2d82fcb4dd99e5838",
|
||||
"transactionHash": "0x1f321cf880f5ec5b189cb1ac1c1c1b71ef8d854fc059c345fea6e6129bb19645"
|
||||
},
|
||||
"1529327661534": {
|
||||
"1530013596495": {
|
||||
"events": {},
|
||||
"links": {},
|
||||
"address": "0xa2a1eb294e1191c1e374f9015a37db35c49e9f26",
|
||||
"transactionHash": "0x5cd2551fce05a8b864a085616ee54449fdddd56dcdebf1019bebd0839c69ef5f"
|
||||
"address": "0x18a8eaa498a58752858e5a912c8ff136b0bc6c69",
|
||||
"transactionHash": "0x7da393438ee13b991e6d94278addb64e86b0c13101efb8a0f0f00d9f86ba51d0"
|
||||
},
|
||||
"1529333415702": {
|
||||
"1530525742205": {
|
||||
"events": {},
|
||||
"links": {},
|
||||
"address": "0xaba1a0da223a2a5e04158bd80b2af7671e27e2c6",
|
||||
"transactionHash": "0x6e7c0c3947a3a851738677ae63915781ce79fdfb0453030fc63bbcd346f2cf89"
|
||||
"address": "0x0a25374d1738169d8653e6ee01f464bf4cdc8dea",
|
||||
"transactionHash": "0x2d7f72a94ced345c372040faa1f51a51f3e8da27a02dd04b9eefef487de3ba05"
|
||||
},
|
||||
"1529652204341": {
|
||||
"1530611935189": {
|
||||
"events": {},
|
||||
"links": {},
|
||||
"address": "0x2013688de17569d52fc4cd4fe5f18821d8b32b2a",
|
||||
"address": "0x0b98f9d8fbd164d8d4884a997a3ef8e7275d5e0e",
|
||||
"transactionHash": "0x2d7f72a94ced345c372040faa1f51a51f3e8da27a02dd04b9eefef487de3ba05"
|
||||
}
|
||||
},
|
||||
"schemaVersion": "2.0.0",
|
||||
"updatedAt": "2018-06-22T07:24:16.274Z"
|
||||
"updatedAt": "2018-07-03T09:59:18.515Z"
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,783 @@
|
|||
{
|
||||
"contractName": "SecuredTokenTransfer",
|
||||
"abi": [],
|
||||
"bytecode": "0x6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00a165627a7a723058203704d691dd103c6cda573e69dcd48a2de4d0edf672da44096a50180687c79a770029",
|
||||
"deployedBytecode": "0x6080604052600080fd00a165627a7a723058203704d691dd103c6cda573e69dcd48a2de4d0edf672da44096a50180687c79a770029",
|
||||
"sourceMap": "133:1051:15:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;133:1051:15;;;;;;;",
|
||||
"deployedSourceMap": "133:1051:15:-;;;;;",
|
||||
"source": "pragma solidity 0.4.24;\n\n\n/// @title SecuredTokenTransfer - Secure token transfer\n/// @author Richard Meissner - <richard@gnosis.pm>\ncontract SecuredTokenTransfer {\n\n /// @dev Transfers a token and returns if it was a success\n /// @param token Token that should be transferred\n /// @param receiver Receiver to whom the token should be transferred\n /// @param amount The amount of tokens that should be transferred\n function transferToken (\n address token, \n address receiver,\n uint256 amount\n )\n internal\n returns (bool transferred)\n {\n bytes memory data = abi.encodeWithSignature(\"transfer(address,uint256)\", receiver, amount);\n // solium-disable-next-line security/no-inline-assembly\n assembly {\n let success := call(sub(gas, 10000), token, 0, add(data, 0x20), mload(data), 0, 0)\n let ptr := mload(0x40)\n returndatacopy(ptr, 0, returndatasize)\n switch returndatasize \n case 0 { transferred := success }\n case 0x20 { transferred := iszero(or(iszero(success), iszero(mload(ptr)))) }\n default { transferred := 0 }\n }\n }\n}\n",
|
||||
"sourcePath": "/Users/apanizo/git/gnosis/safe-contracts/contracts/SecuredTokenTransfer.sol",
|
||||
"ast": {
|
||||
"absolutePath": "/Users/apanizo/git/gnosis/safe-contracts/contracts/SecuredTokenTransfer.sol",
|
||||
"exportedSymbols": {
|
||||
"SecuredTokenTransfer": [
|
||||
3048
|
||||
]
|
||||
},
|
||||
"id": 3049,
|
||||
"nodeType": "SourceUnit",
|
||||
"nodes": [
|
||||
{
|
||||
"id": 3025,
|
||||
"literals": [
|
||||
"solidity",
|
||||
"0.4",
|
||||
".24"
|
||||
],
|
||||
"nodeType": "PragmaDirective",
|
||||
"src": "0:23:15"
|
||||
},
|
||||
{
|
||||
"baseContracts": [],
|
||||
"contractDependencies": [],
|
||||
"contractKind": "contract",
|
||||
"documentation": "@title SecuredTokenTransfer - Secure token transfer\n @author Richard Meissner - <richard@gnosis.pm>",
|
||||
"fullyImplemented": true,
|
||||
"id": 3048,
|
||||
"linearizedBaseContracts": [
|
||||
3048
|
||||
],
|
||||
"name": "SecuredTokenTransfer",
|
||||
"nodeType": "ContractDefinition",
|
||||
"nodes": [
|
||||
{
|
||||
"body": {
|
||||
"id": 3046,
|
||||
"nodeType": "Block",
|
||||
"src": "590:592:15",
|
||||
"statements": [
|
||||
{
|
||||
"assignments": [
|
||||
3037
|
||||
],
|
||||
"declarations": [
|
||||
{
|
||||
"constant": false,
|
||||
"id": 3037,
|
||||
"name": "data",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"scope": 3047,
|
||||
"src": "600:17:15",
|
||||
"stateVariable": false,
|
||||
"storageLocation": "memory",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_bytes_memory_ptr",
|
||||
"typeString": "bytes"
|
||||
},
|
||||
"typeName": {
|
||||
"id": 3036,
|
||||
"name": "bytes",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "600:5:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_bytes_storage_ptr",
|
||||
"typeString": "bytes"
|
||||
}
|
||||
},
|
||||
"value": null,
|
||||
"visibility": "internal"
|
||||
}
|
||||
],
|
||||
"id": 3044,
|
||||
"initialValue": {
|
||||
"argumentTypes": null,
|
||||
"arguments": [
|
||||
{
|
||||
"argumentTypes": null,
|
||||
"hexValue": "7472616e7366657228616464726573732c75696e7432353629",
|
||||
"id": 3040,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": true,
|
||||
"kind": "string",
|
||||
"lValueRequested": false,
|
||||
"nodeType": "Literal",
|
||||
"src": "644:27:15",
|
||||
"subdenomination": null,
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_stringliteral_a9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b",
|
||||
"typeString": "literal_string \"transfer(address,uint256)\""
|
||||
},
|
||||
"value": "transfer(address,uint256)"
|
||||
},
|
||||
{
|
||||
"argumentTypes": null,
|
||||
"id": 3041,
|
||||
"name": "receiver",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 3029,
|
||||
"src": "673:8:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
}
|
||||
},
|
||||
{
|
||||
"argumentTypes": null,
|
||||
"id": 3042,
|
||||
"name": "amount",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 3031,
|
||||
"src": "683:6:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_uint256",
|
||||
"typeString": "uint256"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expression": {
|
||||
"argumentTypes": [
|
||||
{
|
||||
"typeIdentifier": "t_stringliteral_a9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b",
|
||||
"typeString": "literal_string \"transfer(address,uint256)\""
|
||||
},
|
||||
{
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
},
|
||||
{
|
||||
"typeIdentifier": "t_uint256",
|
||||
"typeString": "uint256"
|
||||
}
|
||||
],
|
||||
"expression": {
|
||||
"argumentTypes": null,
|
||||
"id": 3038,
|
||||
"name": "abi",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 4023,
|
||||
"src": "620:3:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_magic_abi",
|
||||
"typeString": "abi"
|
||||
}
|
||||
},
|
||||
"id": 3039,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": true,
|
||||
"lValueRequested": false,
|
||||
"memberName": "encodeWithSignature",
|
||||
"nodeType": "MemberAccess",
|
||||
"referencedDeclaration": null,
|
||||
"src": "620:23:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$",
|
||||
"typeString": "function (string memory) pure returns (bytes memory)"
|
||||
}
|
||||
},
|
||||
"id": 3043,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": false,
|
||||
"kind": "functionCall",
|
||||
"lValueRequested": false,
|
||||
"names": [],
|
||||
"nodeType": "FunctionCall",
|
||||
"src": "620:70:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_bytes_memory_ptr",
|
||||
"typeString": "bytes memory"
|
||||
}
|
||||
},
|
||||
"nodeType": "VariableDeclarationStatement",
|
||||
"src": "600:90:15"
|
||||
},
|
||||
{
|
||||
"externalReferences": [
|
||||
{
|
||||
"transferred": {
|
||||
"declaration": 3034,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "1061:11:15",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"declaration": 3037,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "857:4:15",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"token": {
|
||||
"declaration": 3027,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "824:5:15",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"declaration": 3037,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "838:4:15",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"transferred": {
|
||||
"declaration": 3034,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "1012:11:15",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"transferred": {
|
||||
"declaration": 3034,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "1148:11:15",
|
||||
"valueSize": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": 3045,
|
||||
"nodeType": "InlineAssembly",
|
||||
"operations": "{\n let success := call(sub(gas(), 10000), token, 0, add(data, 0x20), mload(data), 0, 0)\n let ptr := mload(0x40)\n returndatacopy(ptr, 0, returndatasize())\n switch returndatasize()\n case 0 {\n transferred := success\n }\n case 0x20 {\n transferred := iszero(or(iszero(success), iszero(mload(ptr))))\n }\n default {\n transferred := 0\n }\n}",
|
||||
"src": "764:418:15"
|
||||
}
|
||||
]
|
||||
},
|
||||
"documentation": "@dev Transfers a token and returns if it was a success\n @param token Token that should be transferred\n @param receiver Receiver to whom the token should be transferred\n @param amount The amount of tokens that should be transferred",
|
||||
"id": 3047,
|
||||
"implemented": true,
|
||||
"isConstructor": false,
|
||||
"isDeclaredConst": false,
|
||||
"modifiers": [],
|
||||
"name": "transferToken",
|
||||
"nodeType": "FunctionDefinition",
|
||||
"parameters": {
|
||||
"id": 3032,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [
|
||||
{
|
||||
"constant": false,
|
||||
"id": 3027,
|
||||
"name": "token",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"scope": 3047,
|
||||
"src": "463:13:15",
|
||||
"stateVariable": false,
|
||||
"storageLocation": "default",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
},
|
||||
"typeName": {
|
||||
"id": 3026,
|
||||
"name": "address",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "463:7:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
}
|
||||
},
|
||||
"value": null,
|
||||
"visibility": "internal"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"id": 3029,
|
||||
"name": "receiver",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"scope": 3047,
|
||||
"src": "487:16:15",
|
||||
"stateVariable": false,
|
||||
"storageLocation": "default",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
},
|
||||
"typeName": {
|
||||
"id": 3028,
|
||||
"name": "address",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "487:7:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
}
|
||||
},
|
||||
"value": null,
|
||||
"visibility": "internal"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"id": 3031,
|
||||
"name": "amount",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"scope": 3047,
|
||||
"src": "513:14:15",
|
||||
"stateVariable": false,
|
||||
"storageLocation": "default",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_uint256",
|
||||
"typeString": "uint256"
|
||||
},
|
||||
"typeName": {
|
||||
"id": 3030,
|
||||
"name": "uint256",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "513:7:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_uint256",
|
||||
"typeString": "uint256"
|
||||
}
|
||||
},
|
||||
"value": null,
|
||||
"visibility": "internal"
|
||||
}
|
||||
],
|
||||
"src": "453:80:15"
|
||||
},
|
||||
"payable": false,
|
||||
"returnParameters": {
|
||||
"id": 3035,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [
|
||||
{
|
||||
"constant": false,
|
||||
"id": 3034,
|
||||
"name": "transferred",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"scope": 3047,
|
||||
"src": "568:16:15",
|
||||
"stateVariable": false,
|
||||
"storageLocation": "default",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_bool",
|
||||
"typeString": "bool"
|
||||
},
|
||||
"typeName": {
|
||||
"id": 3033,
|
||||
"name": "bool",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "568:4:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_bool",
|
||||
"typeString": "bool"
|
||||
}
|
||||
},
|
||||
"value": null,
|
||||
"visibility": "internal"
|
||||
}
|
||||
],
|
||||
"src": "567:18:15"
|
||||
},
|
||||
"scope": 3048,
|
||||
"src": "430:752:15",
|
||||
"stateMutability": "nonpayable",
|
||||
"superFunction": null,
|
||||
"visibility": "internal"
|
||||
}
|
||||
],
|
||||
"scope": 3049,
|
||||
"src": "133:1051:15"
|
||||
}
|
||||
],
|
||||
"src": "0:1185:15"
|
||||
},
|
||||
"legacyAST": {
|
||||
"absolutePath": "/Users/apanizo/git/gnosis/safe-contracts/contracts/SecuredTokenTransfer.sol",
|
||||
"exportedSymbols": {
|
||||
"SecuredTokenTransfer": [
|
||||
3048
|
||||
]
|
||||
},
|
||||
"id": 3049,
|
||||
"nodeType": "SourceUnit",
|
||||
"nodes": [
|
||||
{
|
||||
"id": 3025,
|
||||
"literals": [
|
||||
"solidity",
|
||||
"0.4",
|
||||
".24"
|
||||
],
|
||||
"nodeType": "PragmaDirective",
|
||||
"src": "0:23:15"
|
||||
},
|
||||
{
|
||||
"baseContracts": [],
|
||||
"contractDependencies": [],
|
||||
"contractKind": "contract",
|
||||
"documentation": "@title SecuredTokenTransfer - Secure token transfer\n @author Richard Meissner - <richard@gnosis.pm>",
|
||||
"fullyImplemented": true,
|
||||
"id": 3048,
|
||||
"linearizedBaseContracts": [
|
||||
3048
|
||||
],
|
||||
"name": "SecuredTokenTransfer",
|
||||
"nodeType": "ContractDefinition",
|
||||
"nodes": [
|
||||
{
|
||||
"body": {
|
||||
"id": 3046,
|
||||
"nodeType": "Block",
|
||||
"src": "590:592:15",
|
||||
"statements": [
|
||||
{
|
||||
"assignments": [
|
||||
3037
|
||||
],
|
||||
"declarations": [
|
||||
{
|
||||
"constant": false,
|
||||
"id": 3037,
|
||||
"name": "data",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"scope": 3047,
|
||||
"src": "600:17:15",
|
||||
"stateVariable": false,
|
||||
"storageLocation": "memory",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_bytes_memory_ptr",
|
||||
"typeString": "bytes"
|
||||
},
|
||||
"typeName": {
|
||||
"id": 3036,
|
||||
"name": "bytes",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "600:5:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_bytes_storage_ptr",
|
||||
"typeString": "bytes"
|
||||
}
|
||||
},
|
||||
"value": null,
|
||||
"visibility": "internal"
|
||||
}
|
||||
],
|
||||
"id": 3044,
|
||||
"initialValue": {
|
||||
"argumentTypes": null,
|
||||
"arguments": [
|
||||
{
|
||||
"argumentTypes": null,
|
||||
"hexValue": "7472616e7366657228616464726573732c75696e7432353629",
|
||||
"id": 3040,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": true,
|
||||
"kind": "string",
|
||||
"lValueRequested": false,
|
||||
"nodeType": "Literal",
|
||||
"src": "644:27:15",
|
||||
"subdenomination": null,
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_stringliteral_a9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b",
|
||||
"typeString": "literal_string \"transfer(address,uint256)\""
|
||||
},
|
||||
"value": "transfer(address,uint256)"
|
||||
},
|
||||
{
|
||||
"argumentTypes": null,
|
||||
"id": 3041,
|
||||
"name": "receiver",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 3029,
|
||||
"src": "673:8:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
}
|
||||
},
|
||||
{
|
||||
"argumentTypes": null,
|
||||
"id": 3042,
|
||||
"name": "amount",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 3031,
|
||||
"src": "683:6:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_uint256",
|
||||
"typeString": "uint256"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expression": {
|
||||
"argumentTypes": [
|
||||
{
|
||||
"typeIdentifier": "t_stringliteral_a9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b",
|
||||
"typeString": "literal_string \"transfer(address,uint256)\""
|
||||
},
|
||||
{
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
},
|
||||
{
|
||||
"typeIdentifier": "t_uint256",
|
||||
"typeString": "uint256"
|
||||
}
|
||||
],
|
||||
"expression": {
|
||||
"argumentTypes": null,
|
||||
"id": 3038,
|
||||
"name": "abi",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 4023,
|
||||
"src": "620:3:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_magic_abi",
|
||||
"typeString": "abi"
|
||||
}
|
||||
},
|
||||
"id": 3039,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": true,
|
||||
"lValueRequested": false,
|
||||
"memberName": "encodeWithSignature",
|
||||
"nodeType": "MemberAccess",
|
||||
"referencedDeclaration": null,
|
||||
"src": "620:23:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_function_abiencodewithsignature_pure$_t_string_memory_ptr_$returns$_t_bytes_memory_ptr_$",
|
||||
"typeString": "function (string memory) pure returns (bytes memory)"
|
||||
}
|
||||
},
|
||||
"id": 3043,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": false,
|
||||
"kind": "functionCall",
|
||||
"lValueRequested": false,
|
||||
"names": [],
|
||||
"nodeType": "FunctionCall",
|
||||
"src": "620:70:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_bytes_memory_ptr",
|
||||
"typeString": "bytes memory"
|
||||
}
|
||||
},
|
||||
"nodeType": "VariableDeclarationStatement",
|
||||
"src": "600:90:15"
|
||||
},
|
||||
{
|
||||
"externalReferences": [
|
||||
{
|
||||
"transferred": {
|
||||
"declaration": 3034,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "1061:11:15",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"declaration": 3037,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "857:4:15",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"token": {
|
||||
"declaration": 3027,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "824:5:15",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"declaration": 3037,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "838:4:15",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"transferred": {
|
||||
"declaration": 3034,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "1012:11:15",
|
||||
"valueSize": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"transferred": {
|
||||
"declaration": 3034,
|
||||
"isOffset": false,
|
||||
"isSlot": false,
|
||||
"src": "1148:11:15",
|
||||
"valueSize": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": 3045,
|
||||
"nodeType": "InlineAssembly",
|
||||
"operations": "{\n let success := call(sub(gas(), 10000), token, 0, add(data, 0x20), mload(data), 0, 0)\n let ptr := mload(0x40)\n returndatacopy(ptr, 0, returndatasize())\n switch returndatasize()\n case 0 {\n transferred := success\n }\n case 0x20 {\n transferred := iszero(or(iszero(success), iszero(mload(ptr))))\n }\n default {\n transferred := 0\n }\n}",
|
||||
"src": "764:418:15"
|
||||
}
|
||||
]
|
||||
},
|
||||
"documentation": "@dev Transfers a token and returns if it was a success\n @param token Token that should be transferred\n @param receiver Receiver to whom the token should be transferred\n @param amount The amount of tokens that should be transferred",
|
||||
"id": 3047,
|
||||
"implemented": true,
|
||||
"isConstructor": false,
|
||||
"isDeclaredConst": false,
|
||||
"modifiers": [],
|
||||
"name": "transferToken",
|
||||
"nodeType": "FunctionDefinition",
|
||||
"parameters": {
|
||||
"id": 3032,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [
|
||||
{
|
||||
"constant": false,
|
||||
"id": 3027,
|
||||
"name": "token",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"scope": 3047,
|
||||
"src": "463:13:15",
|
||||
"stateVariable": false,
|
||||
"storageLocation": "default",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
},
|
||||
"typeName": {
|
||||
"id": 3026,
|
||||
"name": "address",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "463:7:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
}
|
||||
},
|
||||
"value": null,
|
||||
"visibility": "internal"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"id": 3029,
|
||||
"name": "receiver",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"scope": 3047,
|
||||
"src": "487:16:15",
|
||||
"stateVariable": false,
|
||||
"storageLocation": "default",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
},
|
||||
"typeName": {
|
||||
"id": 3028,
|
||||
"name": "address",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "487:7:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
}
|
||||
},
|
||||
"value": null,
|
||||
"visibility": "internal"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"id": 3031,
|
||||
"name": "amount",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"scope": 3047,
|
||||
"src": "513:14:15",
|
||||
"stateVariable": false,
|
||||
"storageLocation": "default",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_uint256",
|
||||
"typeString": "uint256"
|
||||
},
|
||||
"typeName": {
|
||||
"id": 3030,
|
||||
"name": "uint256",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "513:7:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_uint256",
|
||||
"typeString": "uint256"
|
||||
}
|
||||
},
|
||||
"value": null,
|
||||
"visibility": "internal"
|
||||
}
|
||||
],
|
||||
"src": "453:80:15"
|
||||
},
|
||||
"payable": false,
|
||||
"returnParameters": {
|
||||
"id": 3035,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [
|
||||
{
|
||||
"constant": false,
|
||||
"id": 3034,
|
||||
"name": "transferred",
|
||||
"nodeType": "VariableDeclaration",
|
||||
"scope": 3047,
|
||||
"src": "568:16:15",
|
||||
"stateVariable": false,
|
||||
"storageLocation": "default",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_bool",
|
||||
"typeString": "bool"
|
||||
},
|
||||
"typeName": {
|
||||
"id": 3033,
|
||||
"name": "bool",
|
||||
"nodeType": "ElementaryTypeName",
|
||||
"src": "568:4:15",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_bool",
|
||||
"typeString": "bool"
|
||||
}
|
||||
},
|
||||
"value": null,
|
||||
"visibility": "internal"
|
||||
}
|
||||
],
|
||||
"src": "567:18:15"
|
||||
},
|
||||
"scope": 3048,
|
||||
"src": "430:752:15",
|
||||
"stateMutability": "nonpayable",
|
||||
"superFunction": null,
|
||||
"visibility": "internal"
|
||||
}
|
||||
],
|
||||
"scope": 3049,
|
||||
"src": "133:1051:15"
|
||||
}
|
||||
],
|
||||
"src": "0:1185:15"
|
||||
},
|
||||
"compiler": {
|
||||
"name": "solc",
|
||||
"version": "0.4.24+commit.e67f0147.Emscripten.clang"
|
||||
},
|
||||
"networks": {},
|
||||
"schemaVersion": "2.0.0",
|
||||
"updatedAt": "2018-06-29T09:01:22.084Z"
|
||||
}
|
|
@ -3,29 +3,29 @@
|
|||
"abi": [],
|
||||
"bytecode": "0x6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00a165627a7a72305820ec80f1b4520aa5197e4181778f1e2e4fc460002d4a40e2e8e6709c8986067c220029",
|
||||
"deployedBytecode": "0x6080604052600080fd00a165627a7a72305820ec80f1b4520aa5197e4181778f1e2e4fc460002d4a40e2e8e6709c8986067c220029",
|
||||
"sourceMap": "152:166:8:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;152:166:8;;;;;;;",
|
||||
"deployedSourceMap": "152:166:8:-;;;;;",
|
||||
"sourceMap": "152:166:16:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;152:166:16;;;;;;;",
|
||||
"deployedSourceMap": "152:166:16:-;;;;;",
|
||||
"source": "pragma solidity 0.4.24;\n\n\n/// @title SelfAuthorized - authorizes current contract to perform actions\n/// @author Richard Meissner - <richard@gnosis.pm>\ncontract SelfAuthorized {\n modifier authorized() {\n require(msg.sender == address(this), \"Method can only be called from this contract\");\n _;\n }\n}\n",
|
||||
"sourcePath": "/Users/apanizo/git/gnosis/safe-contracts/contracts/SelfAuthorized.sol",
|
||||
"ast": {
|
||||
"absolutePath": "/Users/apanizo/git/gnosis/safe-contracts/contracts/SelfAuthorized.sol",
|
||||
"exportedSymbols": {
|
||||
"SelfAuthorized": [
|
||||
1449
|
||||
3065
|
||||
]
|
||||
},
|
||||
"id": 1450,
|
||||
"id": 3066,
|
||||
"nodeType": "SourceUnit",
|
||||
"nodes": [
|
||||
{
|
||||
"id": 1434,
|
||||
"id": 3050,
|
||||
"literals": [
|
||||
"solidity",
|
||||
"0.4",
|
||||
".24"
|
||||
],
|
||||
"nodeType": "PragmaDirective",
|
||||
"src": "0:23:8"
|
||||
"src": "0:23:16"
|
||||
},
|
||||
{
|
||||
"baseContracts": [],
|
||||
|
@ -33,18 +33,18 @@
|
|||
"contractKind": "contract",
|
||||
"documentation": "@title SelfAuthorized - authorizes current contract to perform actions\n @author Richard Meissner - <richard@gnosis.pm>",
|
||||
"fullyImplemented": true,
|
||||
"id": 1449,
|
||||
"id": 3065,
|
||||
"linearizedBaseContracts": [
|
||||
1449
|
||||
3065
|
||||
],
|
||||
"name": "SelfAuthorized",
|
||||
"nodeType": "ContractDefinition",
|
||||
"nodes": [
|
||||
{
|
||||
"body": {
|
||||
"id": 1447,
|
||||
"id": 3063,
|
||||
"nodeType": "Block",
|
||||
"src": "204:112:8",
|
||||
"src": "204:112:16",
|
||||
"statements": [
|
||||
{
|
||||
"expression": {
|
||||
|
@ -56,7 +56,7 @@
|
|||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
},
|
||||
"id": 1442,
|
||||
"id": 3058,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": false,
|
||||
|
@ -65,18 +65,18 @@
|
|||
"argumentTypes": null,
|
||||
"expression": {
|
||||
"argumentTypes": null,
|
||||
"id": 1437,
|
||||
"id": 3053,
|
||||
"name": "msg",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 1773,
|
||||
"src": "222:3:8",
|
||||
"referencedDeclaration": 4036,
|
||||
"src": "222:3:16",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_magic_message",
|
||||
"typeString": "msg"
|
||||
}
|
||||
},
|
||||
"id": 1438,
|
||||
"id": 3054,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": false,
|
||||
|
@ -84,7 +84,7 @@
|
|||
"memberName": "sender",
|
||||
"nodeType": "MemberAccess",
|
||||
"referencedDeclaration": null,
|
||||
"src": "222:10:8",
|
||||
"src": "222:10:16",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
|
@ -97,14 +97,14 @@
|
|||
"arguments": [
|
||||
{
|
||||
"argumentTypes": null,
|
||||
"id": 1440,
|
||||
"id": 3056,
|
||||
"name": "this",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 1788,
|
||||
"src": "244:4:8",
|
||||
"referencedDeclaration": 4055,
|
||||
"src": "244:4:16",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_contract$_SelfAuthorized_$1449",
|
||||
"typeIdentifier": "t_contract$_SelfAuthorized_$3065",
|
||||
"typeString": "contract SelfAuthorized"
|
||||
}
|
||||
}
|
||||
|
@ -112,24 +112,24 @@
|
|||
"expression": {
|
||||
"argumentTypes": [
|
||||
{
|
||||
"typeIdentifier": "t_contract$_SelfAuthorized_$1449",
|
||||
"typeIdentifier": "t_contract$_SelfAuthorized_$3065",
|
||||
"typeString": "contract SelfAuthorized"
|
||||
}
|
||||
],
|
||||
"id": 1439,
|
||||
"id": 3055,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": true,
|
||||
"lValueRequested": false,
|
||||
"nodeType": "ElementaryTypeNameExpression",
|
||||
"src": "236:7:8",
|
||||
"src": "236:7:16",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_type$_t_address_$",
|
||||
"typeString": "type(address)"
|
||||
},
|
||||
"typeName": "address"
|
||||
},
|
||||
"id": 1441,
|
||||
"id": 3057,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": false,
|
||||
|
@ -137,13 +137,13 @@
|
|||
"lValueRequested": false,
|
||||
"names": [],
|
||||
"nodeType": "FunctionCall",
|
||||
"src": "236:13:8",
|
||||
"src": "236:13:16",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
}
|
||||
},
|
||||
"src": "222:27:8",
|
||||
"src": "222:27:16",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_bool",
|
||||
"typeString": "bool"
|
||||
|
@ -152,14 +152,14 @@
|
|||
{
|
||||
"argumentTypes": null,
|
||||
"hexValue": "4d6574686f642063616e206f6e6c792062652063616c6c65642066726f6d207468697320636f6e7472616374",
|
||||
"id": 1443,
|
||||
"id": 3059,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": true,
|
||||
"kind": "string",
|
||||
"lValueRequested": false,
|
||||
"nodeType": "Literal",
|
||||
"src": "251:46:8",
|
||||
"src": "251:46:16",
|
||||
"subdenomination": null,
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_stringliteral_c4780ef0a1d41d59bac8c510cf9ada421bccf2b90f75a8e4ba2e8c09e8d72733",
|
||||
|
@ -179,21 +179,21 @@
|
|||
"typeString": "literal_string \"Method can only be called from this contract\""
|
||||
}
|
||||
],
|
||||
"id": 1436,
|
||||
"id": 3052,
|
||||
"name": "require",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [
|
||||
1776,
|
||||
1777
|
||||
4039,
|
||||
4040
|
||||
],
|
||||
"referencedDeclaration": 1777,
|
||||
"src": "214:7:8",
|
||||
"referencedDeclaration": 4040,
|
||||
"src": "214:7:16",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$",
|
||||
"typeString": "function (bool,string memory) pure"
|
||||
}
|
||||
},
|
||||
"id": 1444,
|
||||
"id": 3060,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": false,
|
||||
|
@ -201,62 +201,62 @@
|
|||
"lValueRequested": false,
|
||||
"names": [],
|
||||
"nodeType": "FunctionCall",
|
||||
"src": "214:84:8",
|
||||
"src": "214:84:16",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_tuple$__$",
|
||||
"typeString": "tuple()"
|
||||
}
|
||||
},
|
||||
"id": 1445,
|
||||
"id": 3061,
|
||||
"nodeType": "ExpressionStatement",
|
||||
"src": "214:84:8"
|
||||
"src": "214:84:16"
|
||||
},
|
||||
{
|
||||
"id": 1446,
|
||||
"id": 3062,
|
||||
"nodeType": "PlaceholderStatement",
|
||||
"src": "308:1:8"
|
||||
"src": "308:1:16"
|
||||
}
|
||||
]
|
||||
},
|
||||
"documentation": null,
|
||||
"id": 1448,
|
||||
"id": 3064,
|
||||
"name": "authorized",
|
||||
"nodeType": "ModifierDefinition",
|
||||
"parameters": {
|
||||
"id": 1435,
|
||||
"id": 3051,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [],
|
||||
"src": "201:2:8"
|
||||
"src": "201:2:16"
|
||||
},
|
||||
"src": "182:134:8",
|
||||
"src": "182:134:16",
|
||||
"visibility": "internal"
|
||||
}
|
||||
],
|
||||
"scope": 1450,
|
||||
"src": "152:166:8"
|
||||
"scope": 3066,
|
||||
"src": "152:166:16"
|
||||
}
|
||||
],
|
||||
"src": "0:319:8"
|
||||
"src": "0:319:16"
|
||||
},
|
||||
"legacyAST": {
|
||||
"absolutePath": "/Users/apanizo/git/gnosis/safe-contracts/contracts/SelfAuthorized.sol",
|
||||
"exportedSymbols": {
|
||||
"SelfAuthorized": [
|
||||
1449
|
||||
3065
|
||||
]
|
||||
},
|
||||
"id": 1450,
|
||||
"id": 3066,
|
||||
"nodeType": "SourceUnit",
|
||||
"nodes": [
|
||||
{
|
||||
"id": 1434,
|
||||
"id": 3050,
|
||||
"literals": [
|
||||
"solidity",
|
||||
"0.4",
|
||||
".24"
|
||||
],
|
||||
"nodeType": "PragmaDirective",
|
||||
"src": "0:23:8"
|
||||
"src": "0:23:16"
|
||||
},
|
||||
{
|
||||
"baseContracts": [],
|
||||
|
@ -264,18 +264,18 @@
|
|||
"contractKind": "contract",
|
||||
"documentation": "@title SelfAuthorized - authorizes current contract to perform actions\n @author Richard Meissner - <richard@gnosis.pm>",
|
||||
"fullyImplemented": true,
|
||||
"id": 1449,
|
||||
"id": 3065,
|
||||
"linearizedBaseContracts": [
|
||||
1449
|
||||
3065
|
||||
],
|
||||
"name": "SelfAuthorized",
|
||||
"nodeType": "ContractDefinition",
|
||||
"nodes": [
|
||||
{
|
||||
"body": {
|
||||
"id": 1447,
|
||||
"id": 3063,
|
||||
"nodeType": "Block",
|
||||
"src": "204:112:8",
|
||||
"src": "204:112:16",
|
||||
"statements": [
|
||||
{
|
||||
"expression": {
|
||||
|
@ -287,7 +287,7 @@
|
|||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
},
|
||||
"id": 1442,
|
||||
"id": 3058,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": false,
|
||||
|
@ -296,18 +296,18 @@
|
|||
"argumentTypes": null,
|
||||
"expression": {
|
||||
"argumentTypes": null,
|
||||
"id": 1437,
|
||||
"id": 3053,
|
||||
"name": "msg",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 1773,
|
||||
"src": "222:3:8",
|
||||
"referencedDeclaration": 4036,
|
||||
"src": "222:3:16",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_magic_message",
|
||||
"typeString": "msg"
|
||||
}
|
||||
},
|
||||
"id": 1438,
|
||||
"id": 3054,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": false,
|
||||
|
@ -315,7 +315,7 @@
|
|||
"memberName": "sender",
|
||||
"nodeType": "MemberAccess",
|
||||
"referencedDeclaration": null,
|
||||
"src": "222:10:8",
|
||||
"src": "222:10:16",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
|
@ -328,14 +328,14 @@
|
|||
"arguments": [
|
||||
{
|
||||
"argumentTypes": null,
|
||||
"id": 1440,
|
||||
"id": 3056,
|
||||
"name": "this",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [],
|
||||
"referencedDeclaration": 1788,
|
||||
"src": "244:4:8",
|
||||
"referencedDeclaration": 4055,
|
||||
"src": "244:4:16",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_contract$_SelfAuthorized_$1449",
|
||||
"typeIdentifier": "t_contract$_SelfAuthorized_$3065",
|
||||
"typeString": "contract SelfAuthorized"
|
||||
}
|
||||
}
|
||||
|
@ -343,24 +343,24 @@
|
|||
"expression": {
|
||||
"argumentTypes": [
|
||||
{
|
||||
"typeIdentifier": "t_contract$_SelfAuthorized_$1449",
|
||||
"typeIdentifier": "t_contract$_SelfAuthorized_$3065",
|
||||
"typeString": "contract SelfAuthorized"
|
||||
}
|
||||
],
|
||||
"id": 1439,
|
||||
"id": 3055,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": true,
|
||||
"lValueRequested": false,
|
||||
"nodeType": "ElementaryTypeNameExpression",
|
||||
"src": "236:7:8",
|
||||
"src": "236:7:16",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_type$_t_address_$",
|
||||
"typeString": "type(address)"
|
||||
},
|
||||
"typeName": "address"
|
||||
},
|
||||
"id": 1441,
|
||||
"id": 3057,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": false,
|
||||
|
@ -368,13 +368,13 @@
|
|||
"lValueRequested": false,
|
||||
"names": [],
|
||||
"nodeType": "FunctionCall",
|
||||
"src": "236:13:8",
|
||||
"src": "236:13:16",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_address",
|
||||
"typeString": "address"
|
||||
}
|
||||
},
|
||||
"src": "222:27:8",
|
||||
"src": "222:27:16",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_bool",
|
||||
"typeString": "bool"
|
||||
|
@ -383,14 +383,14 @@
|
|||
{
|
||||
"argumentTypes": null,
|
||||
"hexValue": "4d6574686f642063616e206f6e6c792062652063616c6c65642066726f6d207468697320636f6e7472616374",
|
||||
"id": 1443,
|
||||
"id": 3059,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": true,
|
||||
"kind": "string",
|
||||
"lValueRequested": false,
|
||||
"nodeType": "Literal",
|
||||
"src": "251:46:8",
|
||||
"src": "251:46:16",
|
||||
"subdenomination": null,
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_stringliteral_c4780ef0a1d41d59bac8c510cf9ada421bccf2b90f75a8e4ba2e8c09e8d72733",
|
||||
|
@ -410,21 +410,21 @@
|
|||
"typeString": "literal_string \"Method can only be called from this contract\""
|
||||
}
|
||||
],
|
||||
"id": 1436,
|
||||
"id": 3052,
|
||||
"name": "require",
|
||||
"nodeType": "Identifier",
|
||||
"overloadedDeclarations": [
|
||||
1776,
|
||||
1777
|
||||
4039,
|
||||
4040
|
||||
],
|
||||
"referencedDeclaration": 1777,
|
||||
"src": "214:7:8",
|
||||
"referencedDeclaration": 4040,
|
||||
"src": "214:7:16",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_function_require_pure$_t_bool_$_t_string_memory_ptr_$returns$__$",
|
||||
"typeString": "function (bool,string memory) pure"
|
||||
}
|
||||
},
|
||||
"id": 1444,
|
||||
"id": 3060,
|
||||
"isConstant": false,
|
||||
"isLValue": false,
|
||||
"isPure": false,
|
||||
|
@ -432,42 +432,42 @@
|
|||
"lValueRequested": false,
|
||||
"names": [],
|
||||
"nodeType": "FunctionCall",
|
||||
"src": "214:84:8",
|
||||
"src": "214:84:16",
|
||||
"typeDescriptions": {
|
||||
"typeIdentifier": "t_tuple$__$",
|
||||
"typeString": "tuple()"
|
||||
}
|
||||
},
|
||||
"id": 1445,
|
||||
"id": 3061,
|
||||
"nodeType": "ExpressionStatement",
|
||||
"src": "214:84:8"
|
||||
"src": "214:84:16"
|
||||
},
|
||||
{
|
||||
"id": 1446,
|
||||
"id": 3062,
|
||||
"nodeType": "PlaceholderStatement",
|
||||
"src": "308:1:8"
|
||||
"src": "308:1:16"
|
||||
}
|
||||
]
|
||||
},
|
||||
"documentation": null,
|
||||
"id": 1448,
|
||||
"id": 3064,
|
||||
"name": "authorized",
|
||||
"nodeType": "ModifierDefinition",
|
||||
"parameters": {
|
||||
"id": 1435,
|
||||
"id": 3051,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [],
|
||||
"src": "201:2:8"
|
||||
"src": "201:2:16"
|
||||
},
|
||||
"src": "182:134:8",
|
||||
"src": "182:134:16",
|
||||
"visibility": "internal"
|
||||
}
|
||||
],
|
||||
"scope": 1450,
|
||||
"src": "152:166:8"
|
||||
"scope": 3066,
|
||||
"src": "152:166:16"
|
||||
}
|
||||
],
|
||||
"src": "0:319:8"
|
||||
"src": "0:319:16"
|
||||
},
|
||||
"compiler": {
|
||||
"name": "solc",
|
||||
|
@ -475,5 +475,5 @@
|
|||
},
|
||||
"networks": {},
|
||||
"schemaVersion": "2.0.0",
|
||||
"updatedAt": "2018-06-20T07:57:27.027Z"
|
||||
"updatedAt": "2018-06-29T09:01:22.084Z"
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Ebene_7" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||
<g id="ZWC4wa.tif">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M13.5,26c3.7-6.1,7.2-12,11.1-18.4C28.3,14,31.9,19.9,35.6,26c-3.7,2.2-7.4,4.4-11.1,6.6C20.9,30.4,17.2,28.2,13.5,26z
|
||||
M25.1,20.8c2.7,1.2,5.4,2.5,8.5,3.9c-2.9-4.8-5.6-9.3-8.5-14.1C25.1,14.2,25.1,17.4,25.1,20.8z M15.5,24.7
|
||||
c3.1-1.4,5.8-2.7,8.4-3.8c0-3.4,0-6.6,0-10.2C21,15.4,18.4,19.9,15.5,24.7z M33.8,25.9c-3-1.4-5.8-2.6-8.7-3.9c0,3.1,0,6,0,9.1
|
||||
C28,29.3,30.8,27.7,33.8,25.9z M23.9,22c-3,1.4-5.7,2.6-8.6,3.9c3,1.8,5.8,3.4,8.6,5.1C23.9,27.9,23.9,25,23.9,22z"/>
|
||||
<path d="M24.5,33.3c3.5-2,6.9-4,12.1-7.1c-4.9,6.9-8.4,11.8-12.1,17.1C20.8,38,17.2,33,12.4,26.2C17.8,29.4,21.2,31.4,24.5,33.3z
|
||||
M25.1,40.7c2.7-3.8,5.1-7.3,7.9-11.2c-3,1.8-5.5,3.3-7.9,4.7C25.1,36.4,25.1,38.4,25.1,40.7z M23.9,34.2
|
||||
c-2.4-1.4-4.9-2.9-7.9-4.7c2.8,4,5.2,7.4,7.9,11.1C23.9,38.3,23.9,36.4,23.9,34.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -6,16 +6,16 @@ type ControlProps = {
|
|||
next: string,
|
||||
onPrevious: () => void,
|
||||
firstPage: boolean,
|
||||
submitting: boolean,
|
||||
disabled: boolean,
|
||||
}
|
||||
|
||||
const ControlButtons = ({
|
||||
next, firstPage, onPrevious, submitting,
|
||||
next, firstPage, onPrevious, disabled,
|
||||
}: ControlProps) => (
|
||||
<React.Fragment>
|
||||
<Button
|
||||
type="button"
|
||||
disabled={firstPage || submitting}
|
||||
disabled={firstPage || disabled}
|
||||
onClick={onPrevious}
|
||||
>
|
||||
Back
|
||||
|
@ -24,7 +24,7 @@ const ControlButtons = ({
|
|||
variant="raised"
|
||||
color="primary"
|
||||
type="submit"
|
||||
disabled={submitting}
|
||||
disabled={disabled}
|
||||
>
|
||||
{next}
|
||||
</Button>
|
||||
|
@ -37,16 +37,16 @@ type Props = {
|
|||
onPrevious: () => void,
|
||||
firstPage: boolean,
|
||||
lastPage: boolean,
|
||||
submitting: boolean,
|
||||
disabled: boolean,
|
||||
}
|
||||
|
||||
const Controls = ({
|
||||
finishedTx, finishedButton, onPrevious, firstPage, lastPage, submitting,
|
||||
finishedTx, finishedButton, onPrevious, firstPage, lastPage, disabled,
|
||||
}: Props) => (
|
||||
finishedTx
|
||||
? <React.Fragment>{finishedButton}</React.Fragment>
|
||||
: <ControlButtons
|
||||
submitting={submitting}
|
||||
disabled={disabled}
|
||||
next={lastPage ? 'Finish' : 'Next'}
|
||||
firstPage={firstPage}
|
||||
onPrevious={onPrevious}
|
||||
|
|
|
@ -4,7 +4,6 @@ import FormStep from '@material-ui/core/Step'
|
|||
import StepLabel from '@material-ui/core/StepLabel'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import * as React from 'react'
|
||||
import type { FormApi } from 'react-final-form'
|
||||
import GnoForm from '~/components/forms/GnoForm'
|
||||
import Button from '~/components/layout/Button'
|
||||
import Col from '~/components/layout/Col'
|
||||
|
@ -14,6 +13,7 @@ import Controls from './Controls'
|
|||
export { default as Step } from './Step'
|
||||
|
||||
type Props = {
|
||||
disabledWhenValidating?: boolean,
|
||||
classes: Object,
|
||||
steps: string[],
|
||||
finishedTransaction: boolean,
|
||||
|
@ -21,7 +21,7 @@ type Props = {
|
|||
initialValues?: Object,
|
||||
children: React$Node,
|
||||
onReset?: () => void,
|
||||
onSubmit: (values: Object, form: FormApi, callback: ?(errors: ?Object) => void) => ?Object | Promise<?Object> | void,
|
||||
onSubmit: (values: Object) => Promise<void>,
|
||||
}
|
||||
|
||||
type State = {
|
||||
|
@ -31,6 +31,7 @@ type State = {
|
|||
|
||||
type PageProps = {
|
||||
children: Function,
|
||||
prepareNextInitialProps: (values: Object) => {},
|
||||
}
|
||||
|
||||
class GnoStepper extends React.PureComponent<Props, State> {
|
||||
|
@ -63,8 +64,10 @@ class GnoStepper extends React.PureComponent<Props, State> {
|
|||
}))
|
||||
}
|
||||
|
||||
getPageProps = (pages: React$Node): PageProps => React.Children.toArray(pages)[this.state.page].props
|
||||
|
||||
getActivePageFrom = (pages: React$Node) => {
|
||||
const activePageProps = React.Children.toArray(pages)[this.state.page].props
|
||||
const activePageProps = this.getPageProps(pages)
|
||||
const { children, ...props } = activePageProps
|
||||
|
||||
return children(props)
|
||||
|
@ -77,18 +80,28 @@ class GnoStepper extends React.PureComponent<Props, State> {
|
|||
return activePage.props.validate ? activePage.props.validate(values) : {}
|
||||
}
|
||||
|
||||
next = (values: Object) =>
|
||||
next = async (values: Object) => {
|
||||
const activePageProps = this.getPageProps(this.props.children)
|
||||
const { prepareNextInitialProps } = activePageProps
|
||||
|
||||
let pageInitialProps
|
||||
if (prepareNextInitialProps) {
|
||||
pageInitialProps = await prepareNextInitialProps(values)
|
||||
}
|
||||
|
||||
const finalValues = { ...values, ...pageInitialProps }
|
||||
this.setState(state => ({
|
||||
page: Math.min(state.page + 1, React.Children.count(this.props.children) - 1),
|
||||
values,
|
||||
values: finalValues,
|
||||
}))
|
||||
}
|
||||
|
||||
previous = () =>
|
||||
this.setState(state => ({
|
||||
page: Math.max(state.page - 1, 0),
|
||||
}))
|
||||
|
||||
handleSubmit = (values: Object) => {
|
||||
handleSubmit = async (values: Object) => {
|
||||
const { children, onSubmit } = this.props
|
||||
const { page } = this.state
|
||||
const isLastPage = page === React.Children.count(children) - 1
|
||||
|
@ -101,7 +114,7 @@ class GnoStepper extends React.PureComponent<Props, State> {
|
|||
|
||||
render() {
|
||||
const {
|
||||
steps, children, finishedTransaction, finishedButton, classes,
|
||||
steps, children, finishedTransaction, finishedButton, classes, disabledWhenValidating = false,
|
||||
} = this.props
|
||||
const { page, values } = this.state
|
||||
const activePage = this.getActivePageFrom(children)
|
||||
|
@ -124,20 +137,24 @@ class GnoStepper extends React.PureComponent<Props, State> {
|
|||
validation={this.validate}
|
||||
render={activePage}
|
||||
>
|
||||
{(submitting: boolean) => (
|
||||
<Row align="end" margin="lg" grow>
|
||||
<Col xs={12} center="xs">
|
||||
<Controls
|
||||
submitting={submitting}
|
||||
finishedTx={finishedTransaction}
|
||||
finishedButton={finished}
|
||||
onPrevious={this.previous}
|
||||
firstPage={page === 0}
|
||||
lastPage={isLastPage}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
{(submitting: boolean, validating: boolean) => {
|
||||
const disabled = disabledWhenValidating ? submitting || validating : submitting
|
||||
|
||||
return (
|
||||
<Row align="end" margin="lg" grow>
|
||||
<Col xs={12} center="xs">
|
||||
<Controls
|
||||
disabled={disabled}
|
||||
finishedTx={finishedTransaction}
|
||||
finishedButton={finished}
|
||||
onPrevious={this.previous}
|
||||
firstPage={page === 0}
|
||||
lastPage={isLastPage}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}}
|
||||
</GnoForm>
|
||||
</React.Fragment>
|
||||
)
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import { Field } from 'react-final-form'
|
||||
|
||||
export default Field
|
||||
// $FlowFixMe
|
||||
const GnoField = ({ ...props }): React.Element<*> => <Field {...props} />
|
||||
|
||||
export default GnoField
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import { type FormApi } from 'final-form'
|
||||
import { Form } from 'react-final-form'
|
||||
import type { FormApi } from 'react-final-form'
|
||||
|
||||
export type OnSubmit = (
|
||||
values: Object,
|
||||
form: FormApi,
|
||||
callback: ?(errors: ?Object) => ?Object
|
||||
) => ?Object | Promise<?Object> | void
|
||||
|
||||
type Props = {
|
||||
onSubmit: (values: Object, form: FormApi, callback: ?(errors: ?Object) => void) => ?Object | Promise<?Object> | void,
|
||||
onSubmit: OnSubmit,
|
||||
children: Function,
|
||||
padding: number,
|
||||
validation?: (values: Object) => Object | Promise<Object>,
|
||||
|
@ -29,7 +35,7 @@ const GnoForm = ({
|
|||
render={({ handleSubmit, ...rest }) => (
|
||||
<form onSubmit={handleSubmit} style={stylesBasedOn(padding)}>
|
||||
{render(rest)}
|
||||
{children(rest.submitting, rest.submitSucceeded)}
|
||||
{children(rest.submitting, rest.validating)}
|
||||
</form>
|
||||
)}
|
||||
/>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
// @flow
|
||||
import { getWeb3 } from '~/wallets/getWeb3'
|
||||
import { type FieldValidator } from 'final-form'
|
||||
|
||||
type Field = boolean | string
|
||||
type Field = boolean | string | null | typeof undefined
|
||||
|
||||
export const required = (value: Field) => (value ? undefined : 'Required')
|
||||
|
||||
|
@ -19,6 +20,16 @@ export const greaterThan = (min: number) => (value: string) => {
|
|||
return `Should be greater than ${min}`
|
||||
}
|
||||
|
||||
const regexQuery = /^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i
|
||||
const url = new RegExp(regexQuery)
|
||||
export const mustBeUrl = (value: string) => {
|
||||
if (url.test(value)) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return 'Please, provide a valid url'
|
||||
}
|
||||
|
||||
export const minValue = (min: number) => (value: string) => {
|
||||
if (Number.isNaN(Number(value)) || Number.parseFloat(value) >= Number(min)) {
|
||||
return undefined
|
||||
|
@ -48,15 +59,15 @@ export const ADDRESS_REPEATED_ERROR = 'Address already introduced'
|
|||
export const uniqueAddress = (addresses: string[]) => (value: string) =>
|
||||
(addresses.includes(value) ? ADDRESS_REPEATED_ERROR : undefined)
|
||||
|
||||
export const composeValidators = (...validators: Function[]) => (value: Field) =>
|
||||
export const composeValidators = (...validators: Function[]): FieldValidator => (value: Field) =>
|
||||
validators.reduce((error, validator) => error || validator(value), undefined)
|
||||
|
||||
export const inLimit = (limit: number, base: number, baseText: string) => (value: string) => {
|
||||
export const inLimit = (limit: number, base: number, baseText: string, symbol: string = 'ETH') => (value: string) => {
|
||||
const amount = Number(value)
|
||||
const max = limit - base
|
||||
if (amount <= max) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return `Should not exceed ${max} ETH (amount to reach ${baseText})`
|
||||
return `Should not exceed ${max} ${symbol} (amount to reach ${baseText})`
|
||||
}
|
||||
|
|
|
@ -4,13 +4,18 @@ import Loadable from 'react-loadable'
|
|||
import { Switch, Redirect, Route } from 'react-router-dom'
|
||||
import Loader from '~/components/Loader'
|
||||
import Welcome from './welcome/container'
|
||||
import { SAFELIST_ADDRESS, OPEN_ADDRESS, SAFE_PARAM_ADDRESS, WELCOME_ADDRESS } from './routes'
|
||||
import { SAFELIST_ADDRESS, OPEN_ADDRESS, SAFE_PARAM_ADDRESS, WELCOME_ADDRESS, SETTINS_ADDRESS } from './routes'
|
||||
|
||||
const Safe = Loadable({
|
||||
loader: () => import('./safe/container'),
|
||||
loading: Loader,
|
||||
})
|
||||
|
||||
const Settings = Loadable({
|
||||
loader: () => import('./tokens/container'),
|
||||
loading: Loader,
|
||||
})
|
||||
|
||||
const SafeList = Loadable({
|
||||
loader: () => import('./safeList/container'),
|
||||
loading: Loader,
|
||||
|
@ -22,6 +27,9 @@ const Open = Loadable({
|
|||
})
|
||||
|
||||
const SAFE_ADDRESS = `${SAFELIST_ADDRESS}/:${SAFE_PARAM_ADDRESS}`
|
||||
const SAFE_SETTINGS = `${SAFE_ADDRESS}${SETTINS_ADDRESS}`
|
||||
|
||||
export const settingsUrlFrom = (safeAddress: string) => `${SAFELIST_ADDRESS}/${safeAddress}/settings`
|
||||
|
||||
const Routes = () => (
|
||||
<Switch>
|
||||
|
@ -30,6 +38,7 @@ const Routes = () => (
|
|||
<Route exact path={OPEN_ADDRESS} component={Open} />
|
||||
<Route exact path={SAFELIST_ADDRESS} component={SafeList} />
|
||||
<Route exact path={SAFE_ADDRESS} component={Safe} />
|
||||
<Route exact path={SAFE_SETTINGS} component={Settings} />
|
||||
</Switch>
|
||||
)
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import type { FormApi } from 'react-final-form'
|
||||
import Stepper from '~/components/Stepper'
|
||||
import Confirmation from '~/routes/open/components/FormConfirmation'
|
||||
import Review from '~/routes/open/components/ReviewInformation'
|
||||
|
@ -21,8 +20,7 @@ type Props = {
|
|||
userAccount: string,
|
||||
safeAddress: string,
|
||||
safeTx: string,
|
||||
onCallSafeContractSubmit: (values: Object, form: FormApi, callback: ?(errors: ?Object) => void)
|
||||
=> ?Object | Promise<?Object> | void,
|
||||
onCallSafeContractSubmit: (values: Object) => Promise<void>,
|
||||
}
|
||||
|
||||
const Layout = ({
|
||||
|
|
|
@ -26,7 +26,7 @@ storiesOf('Routes /open', module)
|
|||
getProviderInfo()
|
||||
const provider = 'METAMASK'
|
||||
const userAccount = '0x03db1a8b26d08df23337e9276a36b474510f0023'
|
||||
const onCallSafeContractSubmit = async (values: Object) => {
|
||||
const onCallSafeContractSubmit = async (values: Object): Promise<void> => {
|
||||
const accounts = getAccountsFrom(values)
|
||||
const numConfirmations = getThresholdFrom(values)
|
||||
const data = {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import { Field } from 'react-final-form'
|
||||
import Field from '~/components/forms/Field'
|
||||
import TextField from '~/components/forms/TextField'
|
||||
import { composeValidators, minValue, mustBeInteger, required } from '~/components/forms/validator'
|
||||
import Block from '~/components/layout/Block'
|
||||
|
|
|
@ -8,7 +8,7 @@ import { getProviderInfo } from '~/wallets/getWeb3'
|
|||
import Wrapper from '~/test/utils/Wrapper'
|
||||
import { CONFIRMATIONS_ERROR } from '~/routes/open/components/SafeForm'
|
||||
|
||||
const obSubmitMock = () => {}
|
||||
const onSubmitMock = async (): Promise<void> => {}
|
||||
|
||||
describe('React DOM TESTS > Create Safe form', () => {
|
||||
let open
|
||||
|
@ -25,7 +25,7 @@ describe('React DOM TESTS > Create Safe form', () => {
|
|||
userAccount="foo"
|
||||
safeAddress=""
|
||||
safeTx=""
|
||||
onCallSafeContractSubmit={obSubmitMock}
|
||||
onCallSafeContractSubmit={onSubmitMock}
|
||||
/>
|
||||
</Wrapper>
|
||||
))
|
||||
|
|
|
@ -16,12 +16,12 @@ type Props = Actions & {
|
|||
userAccount: string,
|
||||
}
|
||||
|
||||
type State = {
|
||||
export type OpenState = {
|
||||
safeAddress: string,
|
||||
safeTx: string,
|
||||
}
|
||||
|
||||
const createSafe = async (values: Object, userAccount: string, addSafe: AddSafe): Promise<State> => {
|
||||
export const createSafe = async (values: Object, userAccount: string, addSafe: AddSafe): Promise<OpenState> => {
|
||||
const accounts = getAccountsFrom(values)
|
||||
const numConfirmations = getThresholdFrom(values)
|
||||
const name = getSafeNameFrom(values)
|
||||
|
@ -43,7 +43,7 @@ const createSafe = async (values: Object, userAccount: string, addSafe: AddSafe)
|
|||
return { safeAddress: safeContract.address, safeTx: safe }
|
||||
}
|
||||
|
||||
class Open extends React.Component<Props, State> {
|
||||
class Open extends React.Component<Props, OpenState> {
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
|
|
|
@ -3,3 +3,4 @@ export const SAFE_PARAM_ADDRESS = 'address'
|
|||
export const SAFELIST_ADDRESS = '/safes'
|
||||
export const OPEN_ADDRESS = '/open'
|
||||
export const WELCOME_ADDRESS = '/welcome'
|
||||
export const SETTINS_ADDRESS = '/settings'
|
||||
|
|
|
@ -5,7 +5,7 @@ import Stepper from '~/components/Stepper'
|
|||
import { connect } from 'react-redux'
|
||||
import { type Safe } from '~/routes/safe/store/model/safe'
|
||||
import { type Owner, makeOwner } from '~/routes/safe/store/model/owner'
|
||||
import { getSafeEthereumInstance, createTransaction } from '~/routes/safe/component/AddTransaction/createTransactions'
|
||||
import { getSafeEthereumInstance, createTransaction } from '~/wallets/createTransactions'
|
||||
import { setOwners } from '~/utils/localStorage'
|
||||
import AddOwnerForm, { NAME_PARAM, OWNER_ADDRESS_PARAM, INCREASE_PARAM } from './AddOwnerForm'
|
||||
import Review from './Review'
|
||||
|
@ -35,6 +35,18 @@ const getOwnerAddressesFrom = (owners: List<Owner>) => {
|
|||
return owners.map((owner: Owner) => owner.get('address'))
|
||||
}
|
||||
|
||||
export const addOwner = async (values: Object, safe: Safe, threshold: number, executor: string) => {
|
||||
const nonce = Date.now()
|
||||
const newThreshold = values[INCREASE_PARAM] ? threshold + 1 : threshold
|
||||
const newOwnerAddress = values[OWNER_ADDRESS_PARAM]
|
||||
const newOwnerName = values[NAME_PARAM]
|
||||
const safeAddress = safe.get('address')
|
||||
const gnosisSafe = await getSafeEthereumInstance(safeAddress)
|
||||
const data = gnosisSafe.contract.addOwnerWithThreshold.getData(newOwnerAddress, newThreshold)
|
||||
await createTransaction(safe, `Add Owner ${newOwnerName}`, safeAddress, 0, nonce, executor, data)
|
||||
setOwners(safeAddress, safe.get('owners').push(makeOwner({ name: newOwnerName, address: newOwnerAddress })))
|
||||
}
|
||||
|
||||
class AddOwner extends React.Component<Props, State> {
|
||||
state = {
|
||||
done: false,
|
||||
|
@ -45,15 +57,7 @@ class AddOwner extends React.Component<Props, State> {
|
|||
const {
|
||||
safe, threshold, userAddress, fetchTransactions,
|
||||
} = this.props
|
||||
const nonce = Date.now()
|
||||
const newThreshold = values[INCREASE_PARAM] ? threshold + 1 : threshold
|
||||
const newOwnerAddress = values[OWNER_ADDRESS_PARAM]
|
||||
const newOwnerName = values[NAME_PARAM]
|
||||
const safeAddress = safe.get('address')
|
||||
const gnosisSafe = await getSafeEthereumInstance(safeAddress)
|
||||
const data = gnosisSafe.contract.addOwnerWithThreshold.getData(newOwnerAddress, newThreshold)
|
||||
await createTransaction(safe, `Add Owner ${newOwnerName}`, safeAddress, 0, nonce, userAddress, data)
|
||||
setOwners(safeAddress, safe.get('owners').push(makeOwner({ name: newOwnerName, address: newOwnerAddress })))
|
||||
await addOwner(values, safe, threshold, userAddress)
|
||||
fetchTransactions()
|
||||
this.setState({ done: true })
|
||||
} catch (error) {
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
// @flow
|
||||
import { storiesOf } from '@storybook/react'
|
||||
import * as React from 'react'
|
||||
import Stepper from '~/components/Stepper'
|
||||
import styles from '~/components/layout/PageFrame/index.scss'
|
||||
import MultisigForm from './index'
|
||||
|
||||
|
||||
const FrameDecorator = story => (
|
||||
<div className={styles.frame} style={{ textAlign: 'center' }}>
|
||||
{ story() }
|
||||
</div>
|
||||
)
|
||||
|
||||
storiesOf('Components', module)
|
||||
.addDecorator(FrameDecorator)
|
||||
.add('MultisigForm', () => (
|
||||
<Stepper
|
||||
finishedTransaction={false}
|
||||
finishedButton={<Stepper.FinishButton title="SEE TXS" />}
|
||||
onSubmit={() => {}}
|
||||
steps={['Multisig TX Form', 'Review TX']}
|
||||
onReset={() => {}}
|
||||
>
|
||||
<Stepper.Page balance={10}>
|
||||
{ MultisigForm }
|
||||
</Stepper.Page>
|
||||
</Stepper>
|
||||
))
|
|
@ -1,84 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import Stepper from '~/components/Stepper'
|
||||
import { sleep } from '~/utils/timer'
|
||||
import { type Safe } from '~/routes/safe/store/model/safe'
|
||||
import actions, { type Actions } from './actions'
|
||||
import selector, { type SelectorProps } from './selector'
|
||||
import { createTransaction, TX_NAME_PARAM, TX_DESTINATION_PARAM, TX_VALUE_PARAM } from './createTransactions'
|
||||
import MultisigForm from './MultisigForm'
|
||||
import ReviewTx from './ReviewTx'
|
||||
|
||||
const getSteps = () => [
|
||||
'Fill Mutlisig Tx form', 'Review Tx',
|
||||
]
|
||||
|
||||
type Props = SelectorProps & Actions & {
|
||||
safe: Safe,
|
||||
balance: number,
|
||||
onReset: () => void,
|
||||
}
|
||||
|
||||
type State = {
|
||||
done: boolean,
|
||||
}
|
||||
|
||||
export const SEE_TXS_BUTTON_TEXT = 'VISIT TXS'
|
||||
|
||||
class AddTransaction extends React.Component<Props, State> {
|
||||
state = {
|
||||
done: false,
|
||||
}
|
||||
|
||||
onTransaction = async (values: Object) => {
|
||||
try {
|
||||
const { safe, userAddress } = this.props
|
||||
const nonce = Date.now()
|
||||
const destination = values[TX_DESTINATION_PARAM]
|
||||
const value = values[TX_VALUE_PARAM]
|
||||
const name = values[TX_NAME_PARAM]
|
||||
await createTransaction(safe, name, destination, value, nonce, userAddress)
|
||||
await sleep(1500)
|
||||
this.props.fetchTransactions()
|
||||
this.setState({ done: true })
|
||||
} catch (error) {
|
||||
this.setState({ done: false })
|
||||
// eslint-disable-next-line
|
||||
console.log('Error while creating multisig tx ' + error)
|
||||
}
|
||||
}
|
||||
|
||||
onReset = () => {
|
||||
this.setState({ done: false })
|
||||
this.props.onReset() // This is for show the TX list component
|
||||
}
|
||||
|
||||
render() {
|
||||
const { done } = this.state
|
||||
const { balance } = this.props
|
||||
const steps = getSteps()
|
||||
const finishedButton = <Stepper.FinishButton title={SEE_TXS_BUTTON_TEXT} />
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Stepper
|
||||
finishedTransaction={done}
|
||||
finishedButton={finishedButton}
|
||||
onSubmit={this.onTransaction}
|
||||
steps={steps}
|
||||
onReset={this.onReset}
|
||||
>
|
||||
<Stepper.Page balance={balance}>
|
||||
{ MultisigForm }
|
||||
</Stepper.Page>
|
||||
<Stepper.Page>
|
||||
{ ReviewTx }
|
||||
</Stepper.Page>
|
||||
</Stepper>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(selector, actions)(AddTransaction)
|
|
@ -2,7 +2,7 @@
|
|||
import * as React from 'react'
|
||||
import Stepper from '~/components/Stepper'
|
||||
import { connect } from 'react-redux'
|
||||
import { createTransaction } from '~/routes/safe/component/AddTransaction/createTransactions'
|
||||
import { createTransaction } from '~/wallets/createTransactions'
|
||||
import { getEditDailyLimitData, getDailyLimitAddress } from '~/routes/safe/component/Withdraw/withdraw'
|
||||
import { type Safe } from '~/routes/safe/store/model/safe'
|
||||
import EditDailyLimitForm, { EDIT_DAILY_LIMIT_PARAM } from './EditDailyLimitForm'
|
||||
|
|
|
@ -7,11 +7,11 @@ import GnoSafe from './Safe'
|
|||
type Props = SelectorProps
|
||||
|
||||
const Layout = ({
|
||||
safe, balance, provider, userAddress,
|
||||
safe, activeTokens, provider, userAddress,
|
||||
}: Props) => (
|
||||
<React.Fragment>
|
||||
{ safe
|
||||
? <GnoSafe safe={safe} balance={balance} userAddress={userAddress} />
|
||||
? <GnoSafe safe={safe} tokens={activeTokens} userAddress={userAddress} />
|
||||
: <NoSafe provider={provider} text="Not found safe" />
|
||||
}
|
||||
</React.Fragment>
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
// @flow
|
||||
import { storiesOf } from '@storybook/react'
|
||||
import * as React from 'react'
|
||||
import { List } from 'immutable'
|
||||
import styles from '~/components/layout/PageFrame/index.scss'
|
||||
import { SafeFactory } from '~/routes/safe/store/test/builder/safe.builder'
|
||||
import { makeToken } from '~/routes/tokens/store/model/token'
|
||||
import Component from './Layout'
|
||||
|
||||
|
||||
|
@ -12,6 +14,15 @@ const FrameDecorator = story => (
|
|||
</div>
|
||||
)
|
||||
|
||||
const ethBalance = makeToken({
|
||||
address: '0',
|
||||
name: 'Ether',
|
||||
symbol: 'ETH',
|
||||
decimals: 18,
|
||||
logoUrl: 'assets/icons/icon_etherTokens.svg',
|
||||
funds: '2',
|
||||
})
|
||||
|
||||
storiesOf('Routes /safe:address', module)
|
||||
.addDecorator(FrameDecorator)
|
||||
.add('Safe undefined being connected', () => (
|
||||
|
@ -19,7 +30,7 @@ storiesOf('Routes /safe:address', module)
|
|||
userAddress="foo"
|
||||
safe={undefined}
|
||||
provider="METAMASK"
|
||||
balance="0"
|
||||
activeTokens={List([])}
|
||||
fetchBalance={() => {}}
|
||||
/>
|
||||
))
|
||||
|
@ -28,7 +39,7 @@ storiesOf('Routes /safe:address', module)
|
|||
userAddress="foo"
|
||||
safe={undefined}
|
||||
provider=""
|
||||
balance="0"
|
||||
activeTokens={List([])}
|
||||
fetchBalance={() => {}}
|
||||
/>
|
||||
))
|
||||
|
@ -40,7 +51,7 @@ storiesOf('Routes /safe:address', module)
|
|||
userAddress="foo"
|
||||
safe={safe}
|
||||
provider="METAMASK"
|
||||
balance="2"
|
||||
activeTokens={List([]).push(ethBalance)}
|
||||
fetchBalance={() => {}}
|
||||
/>
|
||||
)
|
||||
|
@ -53,7 +64,7 @@ storiesOf('Routes /safe:address', module)
|
|||
userAddress="foo"
|
||||
safe={safe}
|
||||
provider="METAMASK"
|
||||
balance="2"
|
||||
activeTokens={List([]).push(ethBalance)}
|
||||
fetchBalance={() => {}}
|
||||
/>
|
||||
)
|
||||
|
|
|
@ -3,7 +3,7 @@ import * as React from 'react'
|
|||
import Stepper from '~/components/Stepper'
|
||||
import { connect } from 'react-redux'
|
||||
import { type Safe } from '~/routes/safe/store/model/safe'
|
||||
import { getSafeEthereumInstance, createTransaction } from '~/routes/safe/component/AddTransaction/createTransactions'
|
||||
import { getSafeEthereumInstance, createTransaction } from '~/wallets/createTransactions'
|
||||
import RemoveOwnerForm, { DECREASE_PARAM } from './RemoveOwnerForm'
|
||||
import Review from './Review'
|
||||
import selector, { type SelectorProps } from './selector'
|
||||
|
@ -27,11 +27,32 @@ type State = {
|
|||
const SENTINEL_ADDRESS = '0x0000000000000000000000000000000000000001'
|
||||
export const REMOVE_OWNER_RESET_BUTTON_TEXT = 'RESET'
|
||||
|
||||
const initialValuesFrom = (decreaseMandatory: boolean = false) => ({
|
||||
export const initialValuesFrom = (decreaseMandatory: boolean = false) => ({
|
||||
[DECREASE_PARAM]: decreaseMandatory,
|
||||
})
|
||||
|
||||
const shouldDecrease = (numOwners: number, threshold: number) => threshold === numOwners
|
||||
export const shouldDecrease = (numOwners: number, threshold: number) => threshold === numOwners
|
||||
|
||||
export const removeOwner = async (
|
||||
values: Object,
|
||||
safe: Safe,
|
||||
threshold: number,
|
||||
userToRemove: string,
|
||||
name: string,
|
||||
executor: string,
|
||||
) => {
|
||||
const nonce = Date.now()
|
||||
const newThreshold = values[DECREASE_PARAM] ? threshold - 1 : threshold
|
||||
const safeAddress = safe.get('address')
|
||||
const gnosisSafe = await getSafeEthereumInstance(safeAddress)
|
||||
const storedOwners = await gnosisSafe.getOwners()
|
||||
const index = storedOwners.findIndex(ownerAddress => ownerAddress === userToRemove)
|
||||
const prevAddress = index === 0 ? SENTINEL_ADDRESS : storedOwners[index - 1]
|
||||
const data = gnosisSafe.contract.removeOwner.getData(prevAddress, userToRemove, newThreshold)
|
||||
const text = name || userToRemove
|
||||
|
||||
return createTransaction(safe, `Remove Owner ${text}`, safeAddress, 0, nonce, executor, data)
|
||||
}
|
||||
|
||||
class RemoveOwner extends React.Component<Props, State> {
|
||||
state = {
|
||||
|
@ -43,19 +64,7 @@ class RemoveOwner extends React.Component<Props, State> {
|
|||
const {
|
||||
safe, threshold, executor, fetchTransactions, userToRemove, name,
|
||||
} = this.props
|
||||
const nonce = Date.now()
|
||||
const newThreshold = values[DECREASE_PARAM] ? threshold - 1 : threshold
|
||||
const safeAddress = safe.get('address')
|
||||
const gnosisSafe = await getSafeEthereumInstance(safeAddress)
|
||||
|
||||
const storedOwners = await gnosisSafe.getOwners()
|
||||
const index = storedOwners.findIndex(ownerAddress => ownerAddress === userToRemove)
|
||||
const prevAddress = index === 0 ? SENTINEL_ADDRESS : storedOwners[index - 1]
|
||||
const data = gnosisSafe.contract.removeOwner.getData(prevAddress, userToRemove, newThreshold)
|
||||
|
||||
const text = name || userToRemove
|
||||
await createTransaction(safe, `Remove Owner ${text}`, safeAddress, 0, nonce, executor, data)
|
||||
|
||||
await removeOwner(values, safe, threshold, userToRemove, name, executor)
|
||||
fetchTransactions()
|
||||
this.setState({ done: true })
|
||||
} catch (error) {
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import ListItem from '@material-ui/core/ListItem'
|
||||
import ListItemText from '@material-ui/core/ListItemText'
|
||||
import Avatar from '@material-ui/core/Avatar'
|
||||
import AccountBalance from '@material-ui/icons/AccountBalance'
|
||||
|
||||
type Props = {
|
||||
balance: string,
|
||||
}
|
||||
|
||||
const Balance = ({ balance }: Props) => (
|
||||
<ListItem>
|
||||
<Avatar>
|
||||
<AccountBalance />
|
||||
</Avatar>
|
||||
<ListItemText primary="Balance" secondary={`${balance} ETH`} />
|
||||
</ListItem>
|
||||
)
|
||||
|
||||
export default Balance
|
|
@ -0,0 +1,90 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import classNames from 'classnames'
|
||||
import Link from '~/components/layout/Link'
|
||||
import AccountBalance from '@material-ui/icons/AccountBalance'
|
||||
import Settings from '@material-ui/icons/Settings'
|
||||
import Avatar from '@material-ui/core/Avatar'
|
||||
import Collapse from '@material-ui/core/Collapse'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import List from '@material-ui/core/List'
|
||||
import Img from '~/components/layout/Img'
|
||||
import ListItem from '@material-ui/core/ListItem'
|
||||
import ListItemIcon from '@material-ui/core/ListItemIcon'
|
||||
import ListItemText from '@material-ui/core/ListItemText'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import ExpandLess from '@material-ui/icons/ExpandLess'
|
||||
import ExpandMore from '@material-ui/icons/ExpandMore'
|
||||
import { Map } from 'immutable'
|
||||
import Button from '~/components/layout/Button'
|
||||
import openHoc, { type Open } from '~/components/hoc/OpenHoc'
|
||||
import { type WithStyles } from '~/theme/mui'
|
||||
import { type Token } from '~/routes/tokens/store/model/token'
|
||||
import { settingsUrlFrom } from '~/routes'
|
||||
|
||||
type Props = Open & WithStyles & {
|
||||
safeAddress: string,
|
||||
tokens: Map<string, Token>,
|
||||
onMoveFunds: (token: Token) => void,
|
||||
}
|
||||
|
||||
const styles = {
|
||||
nested: {
|
||||
paddingLeft: '40px',
|
||||
},
|
||||
}
|
||||
|
||||
export const MOVE_FUNDS_BUTTON_TEXT = 'Move'
|
||||
|
||||
const BalanceComponent = openHoc(({
|
||||
open, toggle, tokens, classes, onMoveFunds, safeAddress,
|
||||
}: Props) => {
|
||||
const hasBalances = tokens.count() > 0
|
||||
const settingsUrl = settingsUrlFrom(safeAddress)
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ListItem onClick={hasBalances ? toggle : undefined}>
|
||||
<Avatar>
|
||||
<AccountBalance />
|
||||
</Avatar>
|
||||
<ListItemText primary="Balance" secondary="List of different token balances" />
|
||||
<ListItemIcon>
|
||||
<IconButton to={settingsUrl} disabled={!hasBalances} component={Link} className={classes.button}>
|
||||
<Settings />
|
||||
</IconButton>
|
||||
</ListItemIcon>
|
||||
<ListItemIcon>
|
||||
{open
|
||||
? <IconButton disableRipple><ExpandLess /></IconButton>
|
||||
: <IconButton disabled={!hasBalances} disableRipple><ExpandMore /></IconButton>
|
||||
}
|
||||
</ListItemIcon>
|
||||
</ListItem>
|
||||
<Collapse in={open} timeout="auto">
|
||||
<List component="div" disablePadding>
|
||||
{tokens.valueSeq().map((token: Token) => {
|
||||
const symbol = token.get('symbol')
|
||||
const name = token.get('name')
|
||||
const disabled = Number(token.get('funds')) === 0
|
||||
const onMoveFundsClick = () => onMoveFunds(token)
|
||||
|
||||
return (
|
||||
<ListItem key={symbol} className={classNames(classes.nested, symbol)}>
|
||||
<ListItemIcon>
|
||||
<Img src={token.get('logoUrl')} height={30} alt={name} />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={name} secondary={`${token.get('funds')} ${symbol}`} />
|
||||
<Button variant="raised" color="primary" onClick={onMoveFundsClick} disabled={disabled}>
|
||||
{MOVE_FUNDS_BUTTON_TEXT}
|
||||
</Button>
|
||||
</ListItem>
|
||||
)
|
||||
})}
|
||||
</List>
|
||||
</Collapse>
|
||||
</React.Fragment>
|
||||
)
|
||||
})
|
||||
|
||||
export default withStyles(styles)(BalanceComponent)
|
|
@ -11,7 +11,7 @@ type Props = {
|
|||
dailyLimit: DailyLimit,
|
||||
onWithdraw: () => void,
|
||||
onEditDailyLimit: () => void,
|
||||
balance: string,
|
||||
balance: number,
|
||||
}
|
||||
export const EDIT_WITHDRAW = 'Edit'
|
||||
export const WITHDRAW_BUTTON_TEXT = 'Withdraw'
|
||||
|
@ -26,7 +26,7 @@ const DailyLimitComponent = ({
|
|||
const limit = dailyLimit.get('value')
|
||||
const spentToday = dailyLimit.get('spentToday')
|
||||
|
||||
const disabled = spentToday >= limit || Number(balance) === 0
|
||||
const disabled = spentToday >= limit || balance === 0
|
||||
const text = `${limit} ETH (spent today: ${spentToday} ETH)`
|
||||
|
||||
return (
|
||||
|
|
|
@ -7,37 +7,20 @@ import Button from '~/components/layout/Button'
|
|||
import ListItemText from '~/components/List/ListItemText'
|
||||
|
||||
type Props = {
|
||||
balance: string,
|
||||
onAddTx: () => void,
|
||||
onSeeTxs: () => void,
|
||||
}
|
||||
|
||||
export const ADD_MULTISIG_BUTTON_TEXT = 'Add'
|
||||
export const SEE_MULTISIG_BUTTON_TEXT = 'TXs'
|
||||
|
||||
const addStyle = {
|
||||
marginRight: '10px',
|
||||
}
|
||||
|
||||
const DailyLimitComponent = ({ balance, onAddTx, onSeeTxs }: Props) => {
|
||||
const text = `Available ${balance} ETH`
|
||||
const disabled = Number(balance) <= 0
|
||||
const DailyLimitComponent = ({ onSeeTxs }: Props) => {
|
||||
const text = 'See multisig txs executed on this Safe'
|
||||
|
||||
return (
|
||||
<ListItem>
|
||||
<Avatar>
|
||||
<AcoountBalanceWallet />
|
||||
</Avatar>
|
||||
<ListItemText primary="Multisig TXs" secondary={text} />
|
||||
<Button
|
||||
style={addStyle}
|
||||
variant="raised"
|
||||
color="primary"
|
||||
onClick={onAddTx}
|
||||
disabled={disabled}
|
||||
>
|
||||
{ADD_MULTISIG_BUTTON_TEXT}
|
||||
</Button>
|
||||
<ListItemText primary="Safe's Multisig Transaction" secondary={text} />
|
||||
<Button
|
||||
variant="raised"
|
||||
color="primary"
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// @flow
|
||||
import ListComponent from '@material-ui/core/List'
|
||||
import * as React from 'react'
|
||||
import { List } from 'immutable'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Col from '~/components/layout/Col'
|
||||
import Bold from '~/components/layout/Bold'
|
||||
|
@ -7,18 +9,18 @@ import Img from '~/components/layout/Img'
|
|||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import Row from '~/components/layout/Row'
|
||||
import { type Safe } from '~/routes/safe/store/model/safe'
|
||||
import List from '@material-ui/core/List'
|
||||
import { type Token } from '~/routes/tokens/store/model/token'
|
||||
|
||||
import Withdraw from '~/routes/safe/component/Withdraw'
|
||||
import Transactions from '~/routes/safe/component/Transactions'
|
||||
import AddTransaction from '~/routes/safe/component/AddTransaction'
|
||||
import Threshold from '~/routes/safe/component/Threshold'
|
||||
import AddOwner from '~/routes/safe/component/AddOwner'
|
||||
import RemoveOwner from '~/routes/safe/component/RemoveOwner'
|
||||
import EditDailyLimit from '~/routes/safe/component/EditDailyLimit'
|
||||
import SendToken from '~/routes/safe/component/SendToken'
|
||||
|
||||
import Address from './Address'
|
||||
import Balance from './Balance'
|
||||
import BalanceInfo from './BalanceInfo'
|
||||
import Owners from './Owners'
|
||||
import Confirmations from './Confirmations'
|
||||
import DailyLimit from './DailyLimit'
|
||||
|
@ -28,7 +30,7 @@ const safeIcon = require('./assets/gnosis_safe.svg')
|
|||
|
||||
type SafeProps = {
|
||||
safe: Safe,
|
||||
balance: string,
|
||||
tokens: List<Token>,
|
||||
userAddress: string,
|
||||
}
|
||||
|
||||
|
@ -40,6 +42,20 @@ const listStyle = {
|
|||
width: '100%',
|
||||
}
|
||||
|
||||
const getEthBalanceFrom = (tokens: List<Token>) => {
|
||||
const filteredTokens = tokens.filter(token => token.get('symbol') === 'ETH')
|
||||
if (filteredTokens.count() === 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
const ethToken = filteredTokens.get(0)
|
||||
if (!ethToken) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return Number(ethToken.get('funds'))
|
||||
}
|
||||
|
||||
class GnoSafe extends React.PureComponent<SafeProps, State> {
|
||||
state = {
|
||||
component: undefined,
|
||||
|
@ -58,17 +74,10 @@ class GnoSafe extends React.PureComponent<SafeProps, State> {
|
|||
this.setState({ component: <Withdraw safe={safe} dailyLimit={safe.get('dailyLimit')} /> })
|
||||
}
|
||||
|
||||
onAddTx = () => {
|
||||
const { balance, safe } = this.props
|
||||
this.setState({
|
||||
component: <AddTransaction safe={safe} balance={Number(balance)} onReset={this.onListTransactions} />,
|
||||
})
|
||||
}
|
||||
|
||||
onListTransactions = () => {
|
||||
const { safe } = this.props
|
||||
|
||||
this.setState({ component: <Transactions safeName={safe.get('name')} safeAddress={safe.get('address')} onAddTx={this.onAddTx} /> })
|
||||
this.setState({ component: <Transactions safeName={safe.get('name')} safeAddress={safe.get('address')} /> })
|
||||
}
|
||||
|
||||
onEditThreshold = () => {
|
||||
|
@ -89,15 +98,30 @@ class GnoSafe extends React.PureComponent<SafeProps, State> {
|
|||
this.setState({ component: <RemoveOwner safeAddress={safe.get('address')} threshold={safe.get('threshold')} safe={safe} name={name} userToRemove={address} /> })
|
||||
}
|
||||
|
||||
onMoveTokens = (ercToken: Token) => {
|
||||
const { safe } = this.props
|
||||
|
||||
this.setState({
|
||||
component: <SendToken
|
||||
safe={safe}
|
||||
token={ercToken}
|
||||
key={ercToken.get('address')}
|
||||
onReset={this.onListTransactions}
|
||||
/>,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { safe, balance, userAddress } = this.props
|
||||
const { safe, tokens, userAddress } = this.props
|
||||
const { component } = this.state
|
||||
const ethBalance = getEthBalanceFrom(tokens)
|
||||
const address = safe.get('address')
|
||||
|
||||
return (
|
||||
<Row grow>
|
||||
<Col sm={12} top="xs" md={5} margin="xl" overflow>
|
||||
<List style={listStyle}>
|
||||
<Balance balance={balance} />
|
||||
<ListComponent style={listStyle}>
|
||||
<BalanceInfo tokens={tokens} onMoveFunds={this.onMoveTokens} safeAddress={address} />
|
||||
<Owners
|
||||
owners={safe.owners}
|
||||
onAddOwner={this.onAddOwner}
|
||||
|
@ -105,10 +129,10 @@ class GnoSafe extends React.PureComponent<SafeProps, State> {
|
|||
onRemoveOwner={this.onRemoveOwner}
|
||||
/>
|
||||
<Confirmations confirmations={safe.get('threshold')} onEditThreshold={this.onEditThreshold} />
|
||||
<Address address={safe.get('address')} />
|
||||
<DailyLimit balance={balance} dailyLimit={safe.get('dailyLimit')} onWithdraw={this.onWithdraw} onEditDailyLimit={this.onEditDailyLimit} />
|
||||
<MultisigTx balance={balance} onAddTx={this.onAddTx} onSeeTxs={this.onListTransactions} />
|
||||
</List>
|
||||
<Address address={address} />
|
||||
<DailyLimit balance={ethBalance} dailyLimit={safe.get('dailyLimit')} onWithdraw={this.onWithdraw} onEditDailyLimit={this.onEditDailyLimit} />
|
||||
<MultisigTx onSeeTxs={this.onListTransactions} />
|
||||
</ListComponent>
|
||||
</Col>
|
||||
<Col sm={12} center="xs" md={7} margin="xl" layout="column">
|
||||
<Block margin="xl">
|
||||
|
|
|
@ -5,28 +5,29 @@ import Block from '~/components/layout/Block'
|
|||
import Bold from '~/components/layout/Bold'
|
||||
import Heading from '~/components/layout/Heading'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import { TX_NAME_PARAM, TX_DESTINATION_PARAM, TX_VALUE_PARAM } from '~/routes/safe/component/AddTransaction/createTransactions'
|
||||
import { TKN_DESTINATION_PARAM, TKN_VALUE_PARAM } from '~/routes/safe/component/SendToken/SendTokenForm/index'
|
||||
|
||||
type FormProps = {
|
||||
values: Object,
|
||||
submitting: boolean,
|
||||
}
|
||||
|
||||
type Props = {
|
||||
symbol: string
|
||||
}
|
||||
|
||||
const spinnerStyle = {
|
||||
minHeight: '50px',
|
||||
}
|
||||
|
||||
const ReviewTx = () => ({ values, submitting }: FormProps) => (
|
||||
const ReviewTx = ({ symbol }: Props) => ({ values, submitting }: FormProps) => (
|
||||
<Block>
|
||||
<Heading tag="h2">Review the Multisig Tx</Heading>
|
||||
<Heading tag="h2">Review the move token funds</Heading>
|
||||
<Paragraph align="left">
|
||||
<Bold>Transaction Name: </Bold> {values[TX_NAME_PARAM]}
|
||||
<Bold>Destination: </Bold> {values[TKN_DESTINATION_PARAM]}
|
||||
</Paragraph>
|
||||
<Paragraph align="left">
|
||||
<Bold>Destination: </Bold> {values[TX_DESTINATION_PARAM]}
|
||||
</Paragraph>
|
||||
<Paragraph align="left">
|
||||
<Bold>Amount to transfer in ETH: </Bold> {values[TX_VALUE_PARAM]}
|
||||
<Bold>{`Amount to transfer: ${values[TKN_VALUE_PARAM]} ${symbol}`}</Bold>
|
||||
</Paragraph>
|
||||
<Block style={spinnerStyle}>
|
||||
{ submitting && <CircularProgress size={50} /> }
|
|
@ -5,45 +5,28 @@ import TextField from '~/components/forms/TextField'
|
|||
import { composeValidators, inLimit, mustBeFloat, required, greaterThan, mustBeEthereumAddress } from '~/components/forms/validator'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Heading from '~/components/layout/Heading'
|
||||
import { TX_NAME_PARAM, TX_DESTINATION_PARAM, TX_VALUE_PARAM } from '~/routes/safe/component/AddTransaction/createTransactions'
|
||||
|
||||
export const CONFIRMATIONS_ERROR = 'Number of confirmations can not be higher than the number of owners'
|
||||
|
||||
export const safeFieldsValidation = (values: Object) => {
|
||||
const errors = {}
|
||||
|
||||
if (Number.parseInt(values.owners, 10) < Number.parseInt(values.confirmations, 10)) {
|
||||
errors.confirmations = CONFIRMATIONS_ERROR
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
export const TKN_DESTINATION_PARAM = 'tknDestination'
|
||||
export const TKN_VALUE_PARAM = 'tknValue'
|
||||
|
||||
type Props = {
|
||||
balance: number,
|
||||
funds: string,
|
||||
symbol: string,
|
||||
}
|
||||
|
||||
const WithdrawForm = ({ balance }: Props) => () => (
|
||||
const SendTokenForm = ({ funds, symbol }: Props) => () => (
|
||||
<Block margin="md">
|
||||
<Heading tag="h2" margin="lg">
|
||||
Multisig Transaction
|
||||
Send tokens Transaction
|
||||
</Heading>
|
||||
<Heading tag="h4" margin="lg">
|
||||
{`Available balance: ${balance} ETH`}
|
||||
{`Available tokens: ${funds} ${symbol}`}
|
||||
</Heading>
|
||||
<Block margin="md">
|
||||
<Field
|
||||
name={TX_NAME_PARAM}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={required}
|
||||
placeholder="Transaction name"
|
||||
text="Transaction name"
|
||||
/>
|
||||
</Block>
|
||||
<Block margin="md">
|
||||
<Field
|
||||
name={TX_DESTINATION_PARAM}
|
||||
name={TKN_DESTINATION_PARAM}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={composeValidators(required, mustBeEthereumAddress)}
|
||||
|
@ -53,15 +36,15 @@ const WithdrawForm = ({ balance }: Props) => () => (
|
|||
</Block>
|
||||
<Block margin="md">
|
||||
<Field
|
||||
name={TX_VALUE_PARAM}
|
||||
name={TKN_VALUE_PARAM}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={composeValidators(required, mustBeFloat, greaterThan(0), inLimit(balance, 0, 'available balance'))}
|
||||
placeholder="Amount in ETH*"
|
||||
text="Amount in ETH"
|
||||
validate={composeValidators(required, mustBeFloat, greaterThan(0), inLimit(Number(funds), 0, 'available balance', symbol))}
|
||||
placeholder="Amount of tokens*"
|
||||
text="Amount of Tokens"
|
||||
/>
|
||||
</Block>
|
||||
</Block>
|
||||
)
|
||||
|
||||
export default WithdrawForm
|
||||
export default SendTokenForm
|
|
@ -0,0 +1,112 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import { BigNumber } from 'bignumber.js'
|
||||
import { connect } from 'react-redux'
|
||||
import Stepper from '~/components/Stepper'
|
||||
import { sleep } from '~/utils/timer'
|
||||
import { type Safe } from '~/routes/safe/store/model/safe'
|
||||
import { getStandardTokenContract } from '~/routes/tokens/store/actions/fetchTokens'
|
||||
import { type Token } from '~/routes/tokens/store/model/token'
|
||||
import { createTransaction } from '~/wallets/createTransactions'
|
||||
import { EMPTY_DATA } from '~/wallets/ethTransactions'
|
||||
import { toNative } from '~/wallets/tokens'
|
||||
import { isEther } from '~/utils/tokens'
|
||||
import actions, { type Actions } from './actions'
|
||||
import selector, { type SelectorProps } from './selector'
|
||||
import SendTokenForm, { TKN_DESTINATION_PARAM, TKN_VALUE_PARAM } from './SendTokenForm'
|
||||
import ReviewTx from './ReviewTx'
|
||||
|
||||
const getSteps = () => [
|
||||
'Fill Move Token form', 'Review Move Token form',
|
||||
]
|
||||
|
||||
type Props = SelectorProps & Actions & {
|
||||
safe: Safe,
|
||||
token: Token,
|
||||
onReset: () => void,
|
||||
}
|
||||
|
||||
type State = {
|
||||
done: boolean,
|
||||
}
|
||||
|
||||
export const SEE_TXS_BUTTON_TEXT = 'VISIT TXS'
|
||||
|
||||
const getTransferData = async (tokenAddress: string, to: string, amount: BigNumber) => {
|
||||
const StandardToken = await getStandardTokenContract()
|
||||
const myToken = await StandardToken.at(tokenAddress)
|
||||
|
||||
return myToken.contract.transfer.getData(to, amount)
|
||||
}
|
||||
|
||||
const processTokenTransfer = async (safe: Safe, token: Token, to: string, amount: number, userAddress: string) => {
|
||||
const symbol = token.get('symbol')
|
||||
const nonce = Date.now()
|
||||
const name = `Send ${amount} ${symbol} to ${to}`
|
||||
const value = isEther(symbol) ? amount : 0
|
||||
const tokenAddress = token.get('address')
|
||||
const destination = isEther(symbol) ? to : tokenAddress
|
||||
const data = isEther(symbol)
|
||||
? EMPTY_DATA
|
||||
: await getTransferData(tokenAddress, to, await toNative(amount, token.get('decimals')))
|
||||
|
||||
return createTransaction(safe, name, destination, value, nonce, userAddress, data)
|
||||
}
|
||||
|
||||
class SendToken extends React.Component<Props, State> {
|
||||
state = {
|
||||
done: false,
|
||||
}
|
||||
|
||||
onTransaction = async (values: Object) => {
|
||||
try {
|
||||
const { safe, token, userAddress } = this.props
|
||||
|
||||
const amount = values[TKN_VALUE_PARAM]
|
||||
const destination = values[TKN_DESTINATION_PARAM]
|
||||
|
||||
await processTokenTransfer(safe, token, destination, amount, userAddress)
|
||||
await sleep(1500)
|
||||
this.props.fetchTransactions()
|
||||
this.setState({ done: true })
|
||||
} catch (error) {
|
||||
this.setState({ done: false })
|
||||
// eslint-disable-next-line
|
||||
console.log('Error while moving ERC20 token funds ' + error)
|
||||
}
|
||||
}
|
||||
|
||||
onReset = () => {
|
||||
this.setState({ done: false })
|
||||
this.props.onReset() // This is for show the TX list component
|
||||
}
|
||||
|
||||
render() {
|
||||
const { done } = this.state
|
||||
const { token } = this.props
|
||||
const steps = getSteps()
|
||||
const finishedButton = <Stepper.FinishButton title={SEE_TXS_BUTTON_TEXT} />
|
||||
const symbol = token.get('symbol')
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Stepper
|
||||
finishedTransaction={done}
|
||||
finishedButton={finishedButton}
|
||||
onSubmit={this.onTransaction}
|
||||
steps={steps}
|
||||
onReset={this.onReset}
|
||||
>
|
||||
<Stepper.Page funds={token.get('funds')} symbol={symbol}>
|
||||
{ SendTokenForm }
|
||||
</Stepper.Page>
|
||||
<Stepper.Page symbol={symbol}>
|
||||
{ ReviewTx }
|
||||
</Stepper.Page>
|
||||
</Stepper>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(selector, actions)(SendToken)
|
|
@ -2,7 +2,7 @@
|
|||
import * as React from 'react'
|
||||
import Stepper from '~/components/Stepper'
|
||||
import { connect } from 'react-redux'
|
||||
import { getSafeEthereumInstance, createTransaction } from '~/routes/safe/component/AddTransaction/createTransactions'
|
||||
import { getSafeEthereumInstance, createTransaction } from '~/wallets/createTransactions'
|
||||
import { type Safe } from '~/routes/safe/store/model/safe'
|
||||
import ThresholdForm, { THRESHOLD_PARAM } from './ThresholdForm'
|
||||
import selector, { type SelectorProps } from './selector'
|
||||
|
|
|
@ -1,31 +1,17 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import Bold from '~/components/layout/Bold'
|
||||
import Button from '~/components/layout/Button'
|
||||
import Col from '~/components/layout/Col'
|
||||
import Row from '~/components/layout/Row'
|
||||
import Paragraph from '~/components/layout/Paragraph/index'
|
||||
|
||||
type Props = {
|
||||
onAddTx: () => void
|
||||
}
|
||||
|
||||
const NoRights = ({ onAddTx }: Props) => (
|
||||
const NoRights = () => (
|
||||
<Row>
|
||||
<Col xs={12} center="xs" sm={10} smOffset={2} start="sm" margin="md">
|
||||
<Paragraph size="lg">
|
||||
<Bold>No transactions found for this safe</Bold>
|
||||
</Paragraph>
|
||||
</Col>
|
||||
<Col xs={12} center="xs" sm={10} smOffset={2} start="sm" margin="md">
|
||||
<Button
|
||||
onClick={onAddTx}
|
||||
variant="raised"
|
||||
color="primary"
|
||||
>
|
||||
Add Multisig Transaction
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ import selector, { type SelectorProps } from './selector'
|
|||
import actions, { type Actions } from './actions'
|
||||
|
||||
type Props = SelectorProps & Actions & {
|
||||
onAddTx: () => void,
|
||||
safeName: string,
|
||||
safeAddress: string,
|
||||
|
||||
|
@ -25,14 +24,14 @@ class Transactions extends React.Component<Props, {}> {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { transactions, onAddTx, safeName } = this.props
|
||||
const { transactions, safeName } = this.props
|
||||
const hasTransactions = transactions.count() > 0
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{ hasTransactions
|
||||
? transactions.map((tx: Transaction) => <GnoTransaction key={tx.get('nonce')} safeName={safeName} onProcessTx={this.onProcessTx} transaction={tx} />)
|
||||
: <NoTransactions onAddTx={onAddTx} />
|
||||
: <NoTransactions />
|
||||
}
|
||||
</React.Fragment>
|
||||
)
|
||||
|
|
|
@ -7,7 +7,7 @@ import { makeTransaction, type Transaction, type TransactionProps } from '~/rout
|
|||
import { getGnosisSafeContract } from '~/wallets/safeContracts'
|
||||
import { getWeb3 } from '~/wallets/getWeb3'
|
||||
import { sameAddress } from '~/wallets/ethAddresses'
|
||||
import { EXECUTED_CONFIRMATION_HASH } from '~/routes/safe/component/AddTransaction/createTransactions'
|
||||
import { EXECUTED_CONFIRMATION_HASH } from '~/wallets/createTransactions'
|
||||
import { checkReceiptStatus, calculateGasOf, calculateGasPrice } from '~/wallets/ethTransactions'
|
||||
|
||||
export const updateTransaction = (
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
// @flow
|
||||
import { List } from 'immutable'
|
||||
import { getWeb3 } from '~/wallets/getWeb3'
|
||||
import { getGnosisSafeContract, getCreateDailyLimitExtensionContract } from '~/wallets/safeContracts'
|
||||
import { type DailyLimitProps } from '~/routes/safe/store/model/dailyLimit'
|
||||
import { checkReceiptStatus, calculateGasOf, calculateGasPrice } from '~/wallets/ethTransactions'
|
||||
import { checkReceiptStatus, calculateGasOf, calculateGasPrice, EMPTY_DATA } from '~/wallets/ethTransactions'
|
||||
import { type Safe } from '~/routes/safe/store/model/safe'
|
||||
import { buildExecutedConfirmationFrom, storeTransaction } from '~/routes/safe/component/AddTransaction/createTransactions'
|
||||
import { buildExecutedConfirmationFrom, storeTransaction } from '~/wallets/createTransactions'
|
||||
import { type Confirmation } from '~/routes/safe/store/model/confirmation'
|
||||
|
||||
export const LIMIT_POSITION = 0
|
||||
export const SPENT_TODAY_POS = 1
|
||||
|
@ -44,7 +46,7 @@ export const getDailyLimitAddress = async (safeAddress: string) => {
|
|||
return dailyLimitModule.address
|
||||
}
|
||||
|
||||
export const getEditDailyLimitData = async (safeAddress: string, token: string, dailyLimit: string) => {
|
||||
export const getEditDailyLimitData = async (safeAddress: string, token: number, dailyLimit: number) => {
|
||||
const web3 = getWeb3()
|
||||
const dailyLimitModule = await getDailyLimitModuleFrom(safeAddress)
|
||||
const dailyLimitInWei = web3.toWei(dailyLimit, 'ether')
|
||||
|
@ -70,7 +72,7 @@ const withdraw = async (values: Object, safe: Safe, userAccount: string): Promis
|
|||
const nonce = Date.now()
|
||||
const executedConfirmations: List<Confirmation> = buildExecutedConfirmationFrom(safe.get('owners'), userAccount)
|
||||
|
||||
return storeTransaction(`Withdraw movement of ${valueInEth}`, nonce, destination, valueInEth, userAccount, executedConfirmations, txHash.tx, safeAddress, safe.get('threshold'), '0x')
|
||||
return storeTransaction(`Withdraw movement of ${valueInEth}`, nonce, destination, valueInEth, userAccount, executedConfirmations, txHash.tx, safeAddress, safe.get('threshold'), EMPTY_DATA)
|
||||
}
|
||||
|
||||
export default withdraw
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// @flow
|
||||
import { aNewStore } from '~/store'
|
||||
import { addEtherTo } from '~/test/utils/etherMovements'
|
||||
import { addEtherTo } from '~/test/utils/tokenMovements'
|
||||
import { aDeployedSafe, executeWithdrawOn } from '~/routes/safe/store/test/builder/deployedSafe.builder'
|
||||
import { buildMathPropsFrom } from '~/test/utils/buildReactRouterProps'
|
||||
import { safeSelector } from '~/routes/safe/store/selectors/index'
|
||||
import { type Match } from 'react-router-dom'
|
||||
|
||||
describe('Safe Blockchain Test', () => {
|
||||
let store
|
||||
|
@ -21,10 +22,12 @@ describe('Safe Blockchain Test', () => {
|
|||
// WHEN
|
||||
const match: Match = buildMathPropsFrom(safeAddress)
|
||||
const safe = safeSelector(store.getState(), { match })
|
||||
if (!safe) throw new Error()
|
||||
|
||||
await executeWithdrawOn(safe, value)
|
||||
await executeWithdrawOn(safe, value)
|
||||
|
||||
// THEN
|
||||
expect(executeWithdrawOn(safeAddress, value)).rejects.toThrow('VM Exception while processing transaction: revert')
|
||||
expect(executeWithdrawOn(safe, value)).rejects.toThrow('VM Exception while processing transaction: revert')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
// @flow
|
||||
import fetchSafe from '~/routes/safe/store/actions/fetchSafe'
|
||||
import fetchBalance from '~/routes/safe/store/actions/fetchBalance'
|
||||
import { fetchTokens } from '~/routes/tokens/store/actions/fetchTokens'
|
||||
|
||||
export type Actions = {
|
||||
fetchSafe: typeof fetchSafe,
|
||||
fetchBalance: typeof fetchBalance,
|
||||
fetchTokens: typeof fetchTokens,
|
||||
}
|
||||
|
||||
export default {
|
||||
fetchSafe,
|
||||
fetchBalance,
|
||||
fetchTokens,
|
||||
}
|
||||
|
|
|
@ -11,17 +11,32 @@ type Props = Actions & SelectorProps & {
|
|||
granted: boolean,
|
||||
}
|
||||
|
||||
const TIMEOUT = process.env.NODE_ENV === 'test' ? 1500 : 15000
|
||||
|
||||
class SafeView extends React.PureComponent<Props> {
|
||||
componentDidMount() {
|
||||
this.intervalId = setInterval(() => {
|
||||
const { safe, fetchSafe, fetchBalance } = this.props
|
||||
if (!safe) { return }
|
||||
const safeAddress: string = safe.get('address')
|
||||
fetchBalance(safeAddress)
|
||||
if (safe) {
|
||||
fetchSafe(safe)
|
||||
const {
|
||||
safe, fetchTokens, fetchSafe,
|
||||
} = this.props
|
||||
if (!safe) {
|
||||
return
|
||||
}
|
||||
}, 1500)
|
||||
const safeAddress = safe.get('address')
|
||||
fetchTokens(safeAddress)
|
||||
fetchSafe(safe)
|
||||
}, TIMEOUT)
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.safe) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.props.safe) {
|
||||
const safeAddress = this.props.safe.get('address')
|
||||
this.props.fetchTokens(safeAddress)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
@ -32,13 +47,13 @@ class SafeView extends React.PureComponent<Props> {
|
|||
|
||||
render() {
|
||||
const {
|
||||
safe, provider, balance, granted, userAddress,
|
||||
safe, provider, activeTokens, granted, userAddress,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<Page>
|
||||
{ granted
|
||||
? <Layout balance={balance} provider={provider} safe={safe} userAddress={userAddress} />
|
||||
? <Layout activeTokens={activeTokens} provider={provider} safe={safe} userAddress={userAddress} />
|
||||
: <NoRights />
|
||||
}
|
||||
</Page>
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
// @flow
|
||||
import { List } from 'immutable'
|
||||
import { createSelector, createStructuredSelector, type Selector } from 'reselect'
|
||||
import { balanceSelector, safeSelector, type RouterProps, type SafeSelectorProps } from '~/routes/safe/store/selectors'
|
||||
import { safeSelector, type RouterProps, type SafeSelectorProps } from '~/routes/safe/store/selectors'
|
||||
import { providerNameSelector, userAccountSelector } from '~/wallets/store/selectors/index'
|
||||
import { type Safe } from '~/routes/safe/store/model/safe'
|
||||
import { type Owner } from '~/routes/safe/store/model/owner'
|
||||
import { type GlobalState } from '~/store/index'
|
||||
import { sameAddress } from '~/wallets/ethAddresses'
|
||||
import { activeTokensSelector } from '~/routes/tokens/store/selectors'
|
||||
import { type Token } from '~/routes/tokens/store/model/token'
|
||||
|
||||
export type SelectorProps = {
|
||||
safe: SafeSelectorProps,
|
||||
provider: string,
|
||||
balance: string,
|
||||
activeTokens: List<Token>,
|
||||
userAddress: string,
|
||||
}
|
||||
|
||||
|
@ -39,7 +41,7 @@ export const grantedSelector: Selector<GlobalState, RouterProps, boolean> = crea
|
|||
export default createStructuredSelector({
|
||||
safe: safeSelector,
|
||||
provider: providerNameSelector,
|
||||
balance: balanceSelector,
|
||||
activeTokens: activeTokensSelector,
|
||||
granted: grantedSelector,
|
||||
userAddress: userAccountSelector,
|
||||
})
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
// @flow
|
||||
import { createAction } from 'redux-actions'
|
||||
|
||||
export const ADD_BALANCE = 'ADD_BALANCE'
|
||||
|
||||
type BalanceProps = {
|
||||
safeAddress: string,
|
||||
funds: string,
|
||||
}
|
||||
|
||||
const addBalance = createAction(
|
||||
ADD_BALANCE,
|
||||
(safeAddress: string, funds: string): BalanceProps => ({
|
||||
safeAddress,
|
||||
funds,
|
||||
}),
|
||||
)
|
||||
|
||||
export default addBalance
|
|
@ -1,11 +0,0 @@
|
|||
// @flow
|
||||
import type { Dispatch as ReduxDispatch } from 'redux'
|
||||
import { getBalanceInEtherOf } from '~/wallets/getWeb3'
|
||||
import { type GlobalState } from '~/store/index'
|
||||
import addBalance from './addBalance'
|
||||
|
||||
export default (safeAddress: string) => async (dispatch: ReduxDispatch<GlobalState>) => {
|
||||
const balance = await getBalanceInEtherOf(safeAddress)
|
||||
|
||||
return dispatch(addBalance(safeAddress, balance))
|
||||
}
|
|
@ -37,7 +37,14 @@ export const buildSafe = async (storedSafe: Object) => {
|
|||
}
|
||||
|
||||
export default (safe: Safe) => async (dispatch: ReduxDispatch<GlobalState>) => {
|
||||
const safeRecord = await buildSafe(safe.toJSON())
|
||||
try {
|
||||
const safeRecord = await buildSafe(safe.toJSON())
|
||||
|
||||
return dispatch(updateSafe(safeRecord))
|
||||
return dispatch(updateSafe(safeRecord))
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line
|
||||
console.log("Error while updating safe information")
|
||||
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,15 +11,23 @@ const buildSafesFrom = async (loadedSafes: Object): Promise<Map<string, Safe>> =
|
|||
const safes = Map()
|
||||
|
||||
const keys = Object.keys(loadedSafes)
|
||||
const safeRecords = await Promise.all(keys.map((address: string) => buildSafe(loadedSafes[address])))
|
||||
try {
|
||||
const safeRecords = await Promise.all(keys.map((address: string) => buildSafe(loadedSafes[address])))
|
||||
|
||||
return safes.withMutations(async (map) => {
|
||||
safeRecords.forEach((safe: Safe) => map.set(safe.get('address'), safe))
|
||||
})
|
||||
return safes.withMutations(async (map) => {
|
||||
safeRecords.forEach((safe: Safe) => map.set(safe.get('address'), safe))
|
||||
})
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line
|
||||
console.log("Error while fetching safes information")
|
||||
|
||||
return Map()
|
||||
}
|
||||
}
|
||||
|
||||
export default () => async (dispatch: ReduxDispatch<GlobalState>) => {
|
||||
const storedSafes = load(SAFES_KEY)
|
||||
|
||||
const safes = storedSafes ? await buildSafesFrom(storedSafes) : Map()
|
||||
|
||||
return dispatch(updateSafes(safes))
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
// @flow
|
||||
import { Map } from 'immutable'
|
||||
import { handleActions, type ActionType } from 'redux-actions'
|
||||
import addBalance, { ADD_BALANCE } from '~/routes/safe/store/actions/addBalance'
|
||||
|
||||
export const BALANCE_REDUCER_ID = 'balances'
|
||||
|
||||
export type State = Map<string, string>
|
||||
|
||||
export default handleActions({
|
||||
[ADD_BALANCE]: (state: State, action: ActionType<typeof addBalance>): State =>
|
||||
state.set(action.payload.safeAddress, action.payload.funds),
|
||||
}, Map())
|
|
@ -6,7 +6,6 @@ import { type GlobalState } from '~/store/index'
|
|||
import { SAFE_PARAM_ADDRESS } from '~/routes/routes'
|
||||
import { type Safe } from '~/routes/safe/store/model/safe'
|
||||
import { safesMapSelector } from '~/routes/safeList/store/selectors'
|
||||
import { BALANCE_REDUCER_ID } from '~/routes/safe/store/reducer/balances'
|
||||
import { type State as TransactionsState, TRANSACTIONS_REDUCER_ID } from '~/routes/safe/store/reducer/transactions'
|
||||
import { type Transaction } from '~/routes/safe/store/model/transaction'
|
||||
import { type Confirmation } from '~/routes/safe/store/model/confirmation'
|
||||
|
@ -25,14 +24,12 @@ type TransactionProps = {
|
|||
|
||||
const safePropAddressSelector = (state: GlobalState, props: SafeProps) => props.safeAddress
|
||||
|
||||
const safeParamAddressSelector = (state: GlobalState, props: RouterProps) => props.match.params[SAFE_PARAM_ADDRESS] || ''
|
||||
|
||||
const balancesSelector = (state: GlobalState) => state[BALANCE_REDUCER_ID]
|
||||
|
||||
const transactionsSelector = (state: GlobalState): TransactionsState => state[TRANSACTIONS_REDUCER_ID]
|
||||
|
||||
const oneTransactionSelector = (state: GlobalState, props: TransactionProps) => props.transaction
|
||||
|
||||
export const safeParamAddressSelector = (state: GlobalState, props: RouterProps) => props.match.params[SAFE_PARAM_ADDRESS] || ''
|
||||
|
||||
export const safeTransactionsSelector: Selector<GlobalState, SafeProps, List<Transaction>> = createSelector(
|
||||
transactionsSelector,
|
||||
safePropAddressSelector,
|
||||
|
@ -81,18 +78,6 @@ export const safeSelector: Selector<GlobalState, RouterProps, SafeSelectorProps>
|
|||
},
|
||||
)
|
||||
|
||||
export const balanceSelector: Selector<GlobalState, RouterProps, string> = createSelector(
|
||||
balancesSelector,
|
||||
safeParamAddressSelector,
|
||||
(balances: Map<string, string>, address: string) => {
|
||||
if (!address) {
|
||||
return '0'
|
||||
}
|
||||
|
||||
return balances.get(address) || '0'
|
||||
},
|
||||
)
|
||||
|
||||
export default createStructuredSelector({
|
||||
safe: safeSelector,
|
||||
})
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
// @flow
|
||||
import { BALANCE_REDUCER_ID } from '~/routes/safe/store/reducer/balances'
|
||||
import fetchBalance from '~/routes/safe/store/actions/fetchBalance'
|
||||
import { aNewStore } from '~/store'
|
||||
import { addEtherTo } from '~/test/utils/etherMovements'
|
||||
import { aDeployedSafe } from './builder/deployedSafe.builder'
|
||||
|
||||
const balanceReducerTests = () => {
|
||||
describe('Safe Actions[fetchBalance]', () => {
|
||||
let store
|
||||
beforeEach(async () => {
|
||||
store = aNewStore()
|
||||
})
|
||||
|
||||
it('reducer should return 0 to just deployed safe', async () => {
|
||||
// GIVEN
|
||||
const address = await aDeployedSafe(store)
|
||||
|
||||
// WHEN
|
||||
await store.dispatch(fetchBalance(address))
|
||||
|
||||
// THEN
|
||||
const balances = store.getState()[BALANCE_REDUCER_ID]
|
||||
expect(balances).not.toBe(undefined)
|
||||
expect(balances.get(address)).toBe('0')
|
||||
})
|
||||
|
||||
it('reducer should return 1.3456 ETH as funds to safe with 1.3456 ETH', async () => {
|
||||
// GIVEN
|
||||
const address = await aDeployedSafe(store)
|
||||
|
||||
// WHEN
|
||||
await addEtherTo(address, '1.3456')
|
||||
await store.dispatch(fetchBalance(address))
|
||||
|
||||
// THEN
|
||||
const balances = store.getState()[BALANCE_REDUCER_ID]
|
||||
expect(balances).not.toBe(undefined)
|
||||
expect(balances.get(address)).toBe('1.3456')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export default balanceReducerTests
|
|
@ -1,5 +1,6 @@
|
|||
// @flow
|
||||
import addBalance from '~/routes/safe/store/actions/addBalance'
|
||||
/*
|
||||
import addBalances from '~/routes/safe/store/actions/addBalances'
|
||||
import { aNewStore } from '~/store'
|
||||
import { buildMathPropsFrom } from '~/test/utils/buildReactRouterProps'
|
||||
import { balanceSelector } from '../selectors'
|
||||
|
@ -26,7 +27,7 @@ const balanceSelectorTests = () => {
|
|||
const store = aNewStore()
|
||||
|
||||
// WHEN
|
||||
await store.dispatch(addBalance('bar', '1'))
|
||||
await store.dispatch(addBalances('bar', '1'))
|
||||
const balance = balanceSelector(store.getState(), { match })
|
||||
|
||||
// THEN
|
||||
|
@ -40,7 +41,7 @@ const balanceSelectorTests = () => {
|
|||
const store = aNewStore()
|
||||
|
||||
// WHEN
|
||||
await store.dispatch(addBalance(safeAddress, '1.3456'))
|
||||
await store.dispatch(addBalances(safeAddress, '1.3456'))
|
||||
const balance = balanceSelector(store.getState(), { match })
|
||||
|
||||
// THEN
|
||||
|
@ -50,3 +51,4 @@ const balanceSelectorTests = () => {
|
|||
}
|
||||
|
||||
export default balanceSelectorTests
|
||||
*/
|
||||
|
|
|
@ -29,7 +29,7 @@ const grantedSelectorTests = () => {
|
|||
const reduxStore = {
|
||||
safes: Map(),
|
||||
providers: makeProvider(),
|
||||
balances: Map(),
|
||||
tokens: Map(),
|
||||
transactions: Map(),
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ const grantedSelectorTests = () => {
|
|||
const reduxStore = {
|
||||
safes: Map(),
|
||||
providers: makeProvider(),
|
||||
balances: Map(),
|
||||
tokens: Map(),
|
||||
transactions: Map(),
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ const grantedSelectorTests = () => {
|
|||
const reduxStore = {
|
||||
safes: Map(),
|
||||
providers: makeProvider(),
|
||||
balances: Map(),
|
||||
tokens: Map(),
|
||||
transactions: Map(),
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ const grantedSelectorTests = () => {
|
|||
const reduxStore = {
|
||||
[SAFE_REDUCER_ID]: map,
|
||||
providers: makeProvider(provider),
|
||||
balances: undefined,
|
||||
tokens: undefined,
|
||||
transactions: undefined,
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ const grantedSelectorTests = () => {
|
|||
const reduxStore = {
|
||||
[SAFE_REDUCER_ID]: map,
|
||||
providers: makeProvider(provider),
|
||||
balances: undefined,
|
||||
tokens: undefined,
|
||||
transactions: undefined,
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ const grantedSelectorTests = () => {
|
|||
const reduxStore = {
|
||||
[SAFE_REDUCER_ID]: map,
|
||||
providers: makeProvider(provider),
|
||||
balances: undefined,
|
||||
tokens: undefined,
|
||||
transactions: undefined,
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ const safeSelectorTests = () => {
|
|||
const reduxStore = {
|
||||
[SAFE_REDUCER_ID]: Map(),
|
||||
providers: undefined,
|
||||
balances: undefined,
|
||||
tokens: undefined,
|
||||
transactions: undefined,
|
||||
}
|
||||
const match: Match = buildMathPropsFrom('fooAddress')
|
||||
|
@ -38,7 +38,7 @@ const safeSelectorTests = () => {
|
|||
const reduxStore = {
|
||||
[SAFE_REDUCER_ID]: map,
|
||||
providers: undefined,
|
||||
balances: undefined,
|
||||
tokens: undefined,
|
||||
transactions: undefined,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// @flow
|
||||
import balanceReducerTests from './balance.reducer'
|
||||
import safeReducerTests from './safe.reducer'
|
||||
import balanceSelectorTests from './balance.selector'
|
||||
// import balanceSelectorTests from './balance.selector'
|
||||
import safeSelectorTests from './safe.selector'
|
||||
import grantedSelectorTests from './granted.selector'
|
||||
import confirmationsSelectorTests from './confirmations.selector'
|
||||
|
@ -10,13 +9,12 @@ import transactionsSelectorTests from './transactions.selector'
|
|||
describe('Safe Test suite', () => {
|
||||
// ACTIONS AND REDUCERS
|
||||
safeReducerTests()
|
||||
balanceReducerTests()
|
||||
|
||||
// SAFE SELECTOR
|
||||
safeSelectorTests()
|
||||
|
||||
// BALANCE SELECTOR
|
||||
balanceSelectorTests()
|
||||
// balanceSelectorTests()
|
||||
|
||||
// GRANTED SELECTOR
|
||||
grantedSelectorTests()
|
||||
|
|
|
@ -14,7 +14,7 @@ const grantedSelectorTests = () => {
|
|||
const reduxStore = {
|
||||
[SAFE_REDUCER_ID]: Map(),
|
||||
providers: makeProvider(),
|
||||
balances: undefined,
|
||||
tokens: undefined,
|
||||
transactions: Map(),
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ const grantedSelectorTests = () => {
|
|||
const reduxStore = {
|
||||
[SAFE_REDUCER_ID]: Map(),
|
||||
providers: makeProvider(),
|
||||
balances: undefined,
|
||||
tokens: undefined,
|
||||
transactions: Map({ fooAddress: List([transaction]) }),
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ const grantedSelectorTests = () => {
|
|||
const reduxStore = {
|
||||
[SAFE_REDUCER_ID]: Map(),
|
||||
providers: makeProvider(),
|
||||
balances: undefined,
|
||||
tokens: undefined,
|
||||
transactions: Map({ fooAddress: List([transaction]) }),
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,7 @@ const grantedSelectorTests = () => {
|
|||
const reduxStore = {
|
||||
[SAFE_REDUCER_ID]: Map(),
|
||||
providers: makeProvider(),
|
||||
balances: undefined,
|
||||
tokens: undefined,
|
||||
transactions: Map({ fooAddress: List([transaction]) }),
|
||||
}
|
||||
|
||||
|
|
|
@ -1,103 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import TestUtils from 'react-dom/test-utils'
|
||||
import { Provider } from 'react-redux'
|
||||
import { ConnectedRouter } from 'react-router-redux'
|
||||
import Button from '~/components/layout/Button'
|
||||
import { aNewStore, history } from '~/store'
|
||||
import { addEtherTo } from '~/test/utils/etherMovements'
|
||||
import { aDeployedSafe } from '~/routes/safe/store/test/builder/deployedSafe.builder'
|
||||
import { SAFELIST_ADDRESS } from '~/routes/routes'
|
||||
import SafeView from '~/routes/safe/component/Safe'
|
||||
import AppRoutes from '~/routes'
|
||||
import AddTransactionComponent, { SEE_TXS_BUTTON_TEXT } from '~/routes/safe/component/AddTransaction'
|
||||
import TransactionsComponent from '~/routes/safe/component/Transactions'
|
||||
import TransactionComponent from '~/routes/safe/component/Transactions/Transaction'
|
||||
import { getBalanceInEtherOf } from '~/wallets/getWeb3'
|
||||
import { sleep } from '~/utils/timer'
|
||||
import { ADD_MULTISIG_BUTTON_TEXT } from '~/routes/safe/component/Safe/MultisigTx'
|
||||
import { safeTransactionsSelector } from '~/routes/safe/store/selectors/index'
|
||||
import { MOVE_FUNDS_INDEX } from '~/test/builder/safe.dom.utils'
|
||||
|
||||
describe('React DOM TESTS > Withdraw funds from safe', () => {
|
||||
let SafeDom
|
||||
let store
|
||||
let address
|
||||
beforeEach(async () => {
|
||||
// create store
|
||||
store = aNewStore()
|
||||
// deploy safe updating store
|
||||
address = await aDeployedSafe(store)
|
||||
// navigate to SAFE route
|
||||
history.push(`${SAFELIST_ADDRESS}/${address}`)
|
||||
SafeDom = TestUtils.renderIntoDocument((
|
||||
<Provider store={store}>
|
||||
<ConnectedRouter history={history}>
|
||||
<AppRoutes />
|
||||
</ConnectedRouter>
|
||||
</Provider>
|
||||
))
|
||||
})
|
||||
|
||||
it('should execute one transaction if safe has only one owner', async () => {
|
||||
// add funds to safe
|
||||
await addEtherTo(address, '0.1')
|
||||
const Safe = TestUtils.findRenderedComponentWithType(SafeDom, SafeView)
|
||||
|
||||
// $FlowFixMe
|
||||
const buttons = TestUtils.scryRenderedDOMComponentsWithTag(Safe, 'button')
|
||||
const addTxButton = buttons[MOVE_FUNDS_INDEX]
|
||||
expect(addTxButton.getElementsByTagName('span')[0].innerHTML).toEqual(ADD_MULTISIG_BUTTON_TEXT)
|
||||
|
||||
await sleep(1800) // Give time to enable Add button
|
||||
TestUtils.Simulate.click(addTxButton)
|
||||
await sleep(1800) // Give time to render the form
|
||||
const AddTransaction = TestUtils.findRenderedComponentWithType(SafeDom, AddTransactionComponent)
|
||||
|
||||
// $FlowFixMe
|
||||
const inputs = TestUtils.scryRenderedDOMComponentsWithTag(AddTransaction, 'input')
|
||||
const name = inputs[0]
|
||||
const destination = inputs[1]
|
||||
const amountInEth = inputs[2]
|
||||
TestUtils.Simulate.change(name, { target: { value: 'Buying betteries' } })
|
||||
TestUtils.Simulate.change(amountInEth, { target: { value: '0.01' } })
|
||||
TestUtils.Simulate.change(destination, { target: { value: store.getState().providers.account } })
|
||||
|
||||
// $FlowFixMe
|
||||
const form = TestUtils.findRenderedDOMComponentWithTag(AddTransaction, 'form')
|
||||
|
||||
TestUtils.Simulate.submit(form) // fill the form
|
||||
TestUtils.Simulate.submit(form) // confirming data
|
||||
await sleep(4000)
|
||||
|
||||
const safeBalance = await getBalanceInEtherOf(address)
|
||||
expect(safeBalance).toBe('0.09')
|
||||
|
||||
// $FlowFixMe
|
||||
const addTransactionButtons = TestUtils.scryRenderedComponentsWithType(AddTransaction, Button)
|
||||
expect(addTransactionButtons.length).toBe(1)
|
||||
const visitTxsButton = addTransactionButtons[0]
|
||||
|
||||
expect(visitTxsButton.props.children).toEqual(SEE_TXS_BUTTON_TEXT)
|
||||
|
||||
// NOW it is time to check the just executed transaction
|
||||
TestUtils.Simulate.click(TestUtils.scryRenderedDOMComponentsWithTag(visitTxsButton, 'button')[0])
|
||||
|
||||
const Transactions = TestUtils.findRenderedComponentWithType(SafeDom, TransactionsComponent)
|
||||
if (!Transactions) throw new Error()
|
||||
const Transaction = TestUtils.findRenderedComponentWithType(Transactions, TransactionComponent)
|
||||
if (!Transaction) throw new Error()
|
||||
|
||||
const paragraphs = TestUtils.scryRenderedDOMComponentsWithTag(Transaction, 'p')
|
||||
expect(paragraphs[2].innerHTML).toBe('Already executed')
|
||||
TestUtils.Simulate.click(paragraphs[2]) // expanded
|
||||
await sleep(1000) // Time to expand
|
||||
const paragraphsExpanded = TestUtils.scryRenderedDOMComponentsWithTag(Transaction, 'p')
|
||||
const txHashParagraph = paragraphsExpanded[3]
|
||||
|
||||
const transactions = safeTransactionsSelector(store.getState(), { safeAddress: address })
|
||||
const batteryTx = transactions.get(0)
|
||||
if (!batteryTx) throw new Error()
|
||||
expect(txHashParagraph.innerHTML).toBe(batteryTx.get('tx'))
|
||||
})
|
||||
})
|
|
@ -1,62 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import TestUtils from 'react-dom/test-utils'
|
||||
import { Provider } from 'react-redux'
|
||||
import { ConnectedRouter } from 'react-router-redux'
|
||||
import { aNewStore, history } from '~/store'
|
||||
import { aDeployedSafe } from '~/routes/safe/store/test/builder/deployedSafe.builder'
|
||||
import { SAFELIST_ADDRESS } from '~/routes/routes'
|
||||
import AppRoutes from '~/routes'
|
||||
import AddTransactionComponent from '~/routes/safe/component/AddTransaction'
|
||||
import { createMultisigTxFilling, addFundsTo, checkBalanceOf, listTxsOf, getListItemsFrom, expandTransactionOf, getTransactionFromReduxStore, confirmOwners } from '~/routes/safe/test/testMultisig'
|
||||
import { sleep } from '~/utils/timer'
|
||||
|
||||
const renderSafe = localStore => (
|
||||
TestUtils.renderIntoDocument((
|
||||
<Provider store={localStore}>
|
||||
<ConnectedRouter history={history}>
|
||||
<AppRoutes />
|
||||
</ConnectedRouter>
|
||||
</Provider>
|
||||
))
|
||||
)
|
||||
|
||||
describe('React DOM TESTS > Multisig transactions from safe [3 owners & 1 threshold] ', () => {
|
||||
let SafeDom
|
||||
let store
|
||||
let address
|
||||
beforeEach(async () => {
|
||||
// create store
|
||||
store = aNewStore()
|
||||
// deploy safe updating store
|
||||
address = await aDeployedSafe(store, 10, 1, 3)
|
||||
// navigate to SAFE route
|
||||
history.push(`${SAFELIST_ADDRESS}/${address}`)
|
||||
SafeDom = renderSafe(store)
|
||||
})
|
||||
|
||||
it('should execute transaction straight away', async () => {
|
||||
await addFundsTo(SafeDom, address)
|
||||
await checkBalanceOf(address, '0.1')
|
||||
await createMultisigTxFilling(SafeDom, AddTransactionComponent, store)
|
||||
await checkBalanceOf(address, '0.09')
|
||||
await listTxsOf(SafeDom)
|
||||
await sleep(2500)
|
||||
|
||||
await expandTransactionOf(SafeDom, 3, 1)
|
||||
await confirmOwners(SafeDom, 'Adolfo 1 Eth Account [Confirmed]', 'Adolfo 2 Eth Account [Not confirmed]', 'Adolfo 3 Eth Account [Not confirmed]')
|
||||
|
||||
const listItems = getListItemsFrom(SafeDom)
|
||||
|
||||
const status = listItems[2].props.secondary
|
||||
expect(status).toBe('Already executed')
|
||||
|
||||
const confirmed = listItems[3].props.secondary
|
||||
const tx = getTransactionFromReduxStore(store, address)
|
||||
if (!tx) throw new Error()
|
||||
expect(confirmed).toBe(tx.get('tx'))
|
||||
|
||||
const ownerTx = listItems[6].props.secondary
|
||||
expect(ownerTx).toBe('Confirmation hash: EXECUTED')
|
||||
})
|
||||
})
|
|
@ -1,104 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import TestUtils from 'react-dom/test-utils'
|
||||
import { Provider } from 'react-redux'
|
||||
import { ConnectedRouter } from 'react-router-redux'
|
||||
import { aNewStore, history } from '~/store'
|
||||
import { aDeployedSafe } from '~/routes/safe/store/test/builder/deployedSafe.builder'
|
||||
import { SAFELIST_ADDRESS } from '~/routes/routes'
|
||||
import AppRoutes from '~/routes'
|
||||
import { getWeb3 } from '~/wallets/getWeb3'
|
||||
import { sleep } from '~/utils/timer'
|
||||
import { promisify } from '~/utils/promisify'
|
||||
import AddTransactionComponent from '~/routes/safe/component/AddTransaction'
|
||||
import { processTransaction } from '~/routes/safe/component/Transactions/processTransactions'
|
||||
import { confirmationsTransactionSelector } from '~/routes/safe/store/selectors/index'
|
||||
import fetchTransactions from '~/routes/safe/store/actions/fetchTransactions'
|
||||
import { createMultisigTxFilling, addFundsTo, checkBalanceOf, listTxsOf, getListItemsFrom, expandTransactionOf, getTransactionFromReduxStore, confirmOwners } from '~/routes/safe/test/testMultisig'
|
||||
|
||||
const renderSafe = localStore => (
|
||||
TestUtils.renderIntoDocument((
|
||||
<Provider store={localStore}>
|
||||
<ConnectedRouter history={history}>
|
||||
<AppRoutes />
|
||||
</ConnectedRouter>
|
||||
</Provider>
|
||||
))
|
||||
)
|
||||
|
||||
describe('React DOM TESTS > Multisig transactions from safe [3 owners & 3 threshold] ', () => {
|
||||
let SafeDom
|
||||
let store
|
||||
let address
|
||||
let accounts
|
||||
beforeEach(async () => {
|
||||
// create store
|
||||
store = aNewStore()
|
||||
// deploy safe updating store
|
||||
address = await aDeployedSafe(store, 10, 3, 3)
|
||||
// navigate to SAFE route
|
||||
history.push(`${SAFELIST_ADDRESS}/${address}`)
|
||||
SafeDom = renderSafe(store)
|
||||
accounts = await promisify(cb => getWeb3().eth.getAccounts(cb))
|
||||
})
|
||||
|
||||
|
||||
const getAlreadyConfirmed = () => {
|
||||
const tx = getTransactionFromReduxStore(store, address)
|
||||
if (!tx) throw new Error()
|
||||
const confirmed = confirmationsTransactionSelector(store.getState(), { transaction: tx })
|
||||
|
||||
return confirmed
|
||||
}
|
||||
|
||||
const makeConfirmation = async (executor) => {
|
||||
const alreadyConfirmed = getAlreadyConfirmed()
|
||||
const tx = getTransactionFromReduxStore(store, address)
|
||||
if (!tx) throw new Error()
|
||||
await processTransaction(address, tx, alreadyConfirmed, executor)
|
||||
await sleep(800)
|
||||
store.dispatch(fetchTransactions())
|
||||
sleep(1800)
|
||||
SafeDom = renderSafe(store)
|
||||
sleep(1800)
|
||||
await listTxsOf(SafeDom)
|
||||
sleep(800)
|
||||
await expandTransactionOf(SafeDom, 3, 3)
|
||||
sleep(800)
|
||||
}
|
||||
|
||||
it('should execute transaction after 2 owners have confirmed and the last one executed correctly', async () => {
|
||||
await addFundsTo(SafeDom, address)
|
||||
await createMultisigTxFilling(SafeDom, AddTransactionComponent, store)
|
||||
|
||||
await checkBalanceOf(address, '0.1')
|
||||
await listTxsOf(SafeDom)
|
||||
sleep(1400)
|
||||
const listItems = getListItemsFrom(SafeDom)
|
||||
|
||||
const status = listItems[2].props.secondary
|
||||
expect(status).toBe('1 of the 3 confirmations needed')
|
||||
|
||||
const confirmed = listItems[3].props.secondary
|
||||
expect(confirmed).toBe('Waiting for the rest of confirmations')
|
||||
|
||||
await expandTransactionOf(SafeDom, 3, 3)
|
||||
await confirmOwners(SafeDom, 'Adolfo 1 Eth Account [Confirmed]', 'Adolfo 2 Eth Account [Not confirmed]', 'Adolfo 3 Eth Account [Not confirmed]')
|
||||
|
||||
await makeConfirmation(accounts[1])
|
||||
await confirmOwners(SafeDom, 'Adolfo 1 Eth Account [Confirmed]', 'Adolfo 2 Eth Account [Confirmed]', 'Adolfo 3 Eth Account [Not confirmed]')
|
||||
|
||||
await makeConfirmation(accounts[2])
|
||||
await confirmOwners(SafeDom, 'Adolfo 1 Eth Account [Confirmed]', 'Adolfo 2 Eth Account [Confirmed]', 'Adolfo 3 Eth Account [Confirmed]')
|
||||
|
||||
const listItemsExecuted = getListItemsFrom(SafeDom)
|
||||
|
||||
const statusExecuted = listItemsExecuted[2].props.secondary
|
||||
expect(statusExecuted).toBe('Already executed')
|
||||
|
||||
const confirmedExecuted = listItemsExecuted[3].props.secondary
|
||||
const tx = getTransactionFromReduxStore(store, address)
|
||||
if (!tx) throw new Error()
|
||||
expect(confirmedExecuted).toBe(tx.get('tx'))
|
||||
})
|
||||
})
|
|
@ -1,187 +0,0 @@
|
|||
// @flow
|
||||
import { aNewStore } from '~/store'
|
||||
import { aDeployedSafe } from '~/routes/safe/store/test/builder/deployedSafe.builder'
|
||||
import { getWeb3 } from '~/wallets/getWeb3'
|
||||
import { sleep } from '~/utils/timer'
|
||||
import { type Match } from 'react-router-dom'
|
||||
import { promisify } from '~/utils/promisify'
|
||||
import { processTransaction } from '~/routes/safe/component/Transactions/processTransactions'
|
||||
import { confirmationsTransactionSelector, safeSelector, safeTransactionsSelector } from '~/routes/safe/store/selectors/index'
|
||||
import { getTransactionFromReduxStore } from '~/routes/safe/test/testMultisig'
|
||||
import { buildMathPropsFrom } from '~/test/utils/buildReactRouterProps'
|
||||
import { createTransaction } from '~/routes/safe/component/AddTransaction/createTransactions'
|
||||
import fetchTransactions from '~/routes/safe/store/actions/fetchTransactions'
|
||||
import { type GlobalState } from '~/store/index'
|
||||
import { type Safe } from '~/routes/safe/store/model/safe'
|
||||
import { type Transaction } from '~/routes/safe/store/model/transaction'
|
||||
import { getGnosisSafeInstanceAt } from '~/wallets/safeContracts'
|
||||
|
||||
const getSafeFrom = (state: GlobalState, safeAddress: string): Safe => {
|
||||
const match: Match = buildMathPropsFrom(safeAddress)
|
||||
const safe = safeSelector(state, { match })
|
||||
if (!safe) throw new Error()
|
||||
|
||||
return safe
|
||||
}
|
||||
|
||||
describe('React DOM TESTS > Add and remove owners', () => {
|
||||
const assureExecuted = (transaction: Transaction) => {
|
||||
expect(transaction.get('tx')).not.toBe(null)
|
||||
expect(transaction.get('tx')).not.toBe(undefined)
|
||||
expect(transaction.get('tx')).not.toBe('')
|
||||
}
|
||||
|
||||
const assureThresholdIs = async (gnosisSafe, threshold: number) => {
|
||||
const safeThreshold = await gnosisSafe.getThreshold()
|
||||
expect(Number(safeThreshold)).toEqual(threshold)
|
||||
}
|
||||
|
||||
const assureOwnersAre = async (gnosisSafe, ...owners) => {
|
||||
const safeOwners = await gnosisSafe.getOwners()
|
||||
expect(safeOwners.length).toEqual(owners.length)
|
||||
for (let i = 0; i < owners.length; i += 1) {
|
||||
expect(safeOwners[i]).toBe(owners[i])
|
||||
}
|
||||
}
|
||||
|
||||
it('adds owner without increasing the threshold', async () => {
|
||||
// GIVEN
|
||||
const numOwners = 2
|
||||
const threshold = 1
|
||||
const store = aNewStore()
|
||||
const address = await aDeployedSafe(store, 10, threshold, numOwners)
|
||||
const accounts = await promisify(cb => getWeb3().eth.getAccounts(cb))
|
||||
const safe = getSafeFrom(store.getState(), address)
|
||||
const gnosisSafe = await getGnosisSafeInstanceAt(address)
|
||||
|
||||
// WHEN
|
||||
await assureThresholdIs(gnosisSafe, 1)
|
||||
await assureOwnersAre(gnosisSafe, accounts[0], accounts[1])
|
||||
const nonce = Date.now()
|
||||
const accountIndex = 5
|
||||
const data = gnosisSafe.contract.addOwnerWithThreshold.getData(accounts[accountIndex], 1)
|
||||
await createTransaction(safe, `Add Owner with index ${accountIndex}`, address, 0, nonce, accounts[0], data)
|
||||
await sleep(1500)
|
||||
await store.dispatch(fetchTransactions())
|
||||
|
||||
// THEN
|
||||
const transactions = safeTransactionsSelector(store.getState(), { safeAddress: address })
|
||||
expect(transactions.count()).toBe(1)
|
||||
const tx = transactions.get(0)
|
||||
if (!tx) throw new Error()
|
||||
assureExecuted(tx)
|
||||
await assureOwnersAre(gnosisSafe, accounts[5], accounts[0], accounts[1])
|
||||
await assureThresholdIs(gnosisSafe, 1)
|
||||
})
|
||||
|
||||
it('adds owner increasing the threshold', async () => {
|
||||
// GIVEN
|
||||
const numOwners = 2
|
||||
const threshold = 1
|
||||
const store = aNewStore()
|
||||
const address = await aDeployedSafe(store, 10, threshold, numOwners)
|
||||
const accounts = await promisify(cb => getWeb3().eth.getAccounts(cb))
|
||||
const safe = getSafeFrom(store.getState(), address)
|
||||
const gnosisSafe = await getGnosisSafeInstanceAt(address)
|
||||
|
||||
// WHEN
|
||||
await assureThresholdIs(gnosisSafe, 1)
|
||||
await assureOwnersAre(gnosisSafe, accounts[0], accounts[1])
|
||||
const nonce = Date.now()
|
||||
const accountIndex = 5
|
||||
const data = gnosisSafe.contract.addOwnerWithThreshold.getData(accounts[accountIndex], 2)
|
||||
await createTransaction(safe, `Add Owner with index ${accountIndex}`, address, 0, nonce, accounts[0], data)
|
||||
await sleep(1500)
|
||||
await store.dispatch(fetchTransactions())
|
||||
|
||||
// THEN
|
||||
const transactions = safeTransactionsSelector(store.getState(), { safeAddress: address })
|
||||
expect(transactions.count()).toBe(1)
|
||||
const tx = transactions.get(0)
|
||||
if (!tx) throw new Error()
|
||||
assureExecuted(tx)
|
||||
await assureOwnersAre(gnosisSafe, accounts[accountIndex], accounts[0], accounts[1])
|
||||
await assureThresholdIs(gnosisSafe, 2)
|
||||
})
|
||||
|
||||
const processOwnerModification = async (store, safeAddress, executor) => {
|
||||
const tx = getTransactionFromReduxStore(store, safeAddress)
|
||||
if (!tx) throw new Error()
|
||||
const confirmed = confirmationsTransactionSelector(store.getState(), { transaction: tx })
|
||||
const data = tx.get('data')
|
||||
expect(data).not.toBe(null)
|
||||
expect(data).not.toBe(undefined)
|
||||
expect(data).not.toBe('')
|
||||
|
||||
await processTransaction(safeAddress, tx, confirmed, executor)
|
||||
await sleep(1800)
|
||||
}
|
||||
|
||||
it('remove owner without decreasing the threshold', async () => {
|
||||
// GIVEN
|
||||
const numOwners = 3
|
||||
const threshold = 2
|
||||
const store = aNewStore()
|
||||
const address = await aDeployedSafe(store, 10, threshold, numOwners)
|
||||
const accounts = await promisify(cb => getWeb3().eth.getAccounts(cb))
|
||||
const safe = getSafeFrom(store.getState(), address)
|
||||
const gnosisSafe = await getGnosisSafeInstanceAt(address)
|
||||
|
||||
// WHEN
|
||||
await assureThresholdIs(gnosisSafe, 2)
|
||||
await assureOwnersAre(gnosisSafe, accounts[0], accounts[1], accounts[2])
|
||||
const nonce = Date.now()
|
||||
const accountIndex = 2
|
||||
const data = gnosisSafe.contract.removeOwner.getData(accounts[accountIndex - 1], accounts[accountIndex], 2)
|
||||
await createTransaction(safe, `Remove owner Address 3 ${nonce}`, address, 0, nonce, accounts[0], data)
|
||||
await sleep(1500)
|
||||
await assureOwnersAre(gnosisSafe, accounts[0], accounts[1], accounts[2])
|
||||
await store.dispatch(fetchTransactions())
|
||||
|
||||
|
||||
processOwnerModification(store, address, accounts[1])
|
||||
await sleep(3000)
|
||||
await store.dispatch(fetchTransactions())
|
||||
await sleep(3000)
|
||||
const tx = getTransactionFromReduxStore(store, address)
|
||||
if (!tx) throw new Error()
|
||||
const txHash = tx.get('tx')
|
||||
expect(txHash).not.toBe('')
|
||||
await assureThresholdIs(gnosisSafe, 2)
|
||||
await assureOwnersAre(gnosisSafe, accounts[0], accounts[1])
|
||||
})
|
||||
|
||||
it('remove owner decreasing the threshold', async () => {
|
||||
// GIVEN
|
||||
const numOwners = 2
|
||||
const threshold = 2
|
||||
const store = aNewStore()
|
||||
const address = await aDeployedSafe(store, 10, threshold, numOwners)
|
||||
const accounts = await promisify(cb => getWeb3().eth.getAccounts(cb))
|
||||
const safe = getSafeFrom(store.getState(), address)
|
||||
const gnosisSafe = await getGnosisSafeInstanceAt(address)
|
||||
|
||||
// WHEN
|
||||
await assureThresholdIs(gnosisSafe, 2)
|
||||
await assureOwnersAre(gnosisSafe, accounts[0], accounts[1])
|
||||
const nonce = Date.now()
|
||||
const accountIndex = 1
|
||||
const data = gnosisSafe.contract.removeOwner.getData(accounts[accountIndex - 1], accounts[accountIndex], 1)
|
||||
await createTransaction(safe, `Remove owner Address 2 ${nonce}`, address, 0, nonce, accounts[0], data)
|
||||
await sleep(1500)
|
||||
await assureOwnersAre(gnosisSafe, accounts[0], accounts[1])
|
||||
await store.dispatch(fetchTransactions())
|
||||
|
||||
|
||||
processOwnerModification(store, address, accounts[1])
|
||||
await sleep(3000)
|
||||
await store.dispatch(fetchTransactions())
|
||||
await sleep(3000)
|
||||
const tx = getTransactionFromReduxStore(store, address)
|
||||
if (!tx) throw new Error()
|
||||
const txHash = tx.get('tx')
|
||||
expect(txHash).not.toBe('')
|
||||
await assureThresholdIs(gnosisSafe, 1)
|
||||
await assureOwnersAre(gnosisSafe, accounts[0])
|
||||
})
|
||||
})
|
|
@ -9,7 +9,7 @@ import { processTransaction } from '~/routes/safe/component/Transactions/process
|
|||
import { confirmationsTransactionSelector, safeSelector, safeTransactionsSelector } from '~/routes/safe/store/selectors/index'
|
||||
import { getTransactionFromReduxStore } from '~/routes/safe/test/testMultisig'
|
||||
import { buildMathPropsFrom } from '~/test/utils/buildReactRouterProps'
|
||||
import { createTransaction } from '~/routes/safe/component/AddTransaction/createTransactions'
|
||||
import { createTransaction } from '~/wallets/createTransactions'
|
||||
import { getGnosisSafeContract } from '~/wallets/safeContracts'
|
||||
import fetchTransactions from '~/routes/safe/store/actions/fetchTransactions'
|
||||
|
||||
|
|
|
@ -1,125 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import TestUtils from 'react-dom/test-utils'
|
||||
import { Provider } from 'react-redux'
|
||||
import { ConnectedRouter } from 'react-router-redux'
|
||||
import Button from '~/components/layout/Button'
|
||||
import { aNewStore, history } from '~/store'
|
||||
import { addEtherTo } from '~/test/utils/etherMovements'
|
||||
import { aDeployedSafe, executeWithdrawOn } from '~/routes/safe/store/test/builder/deployedSafe.builder'
|
||||
import { SAFELIST_ADDRESS } from '~/routes/routes'
|
||||
import SafeView from '~/routes/safe/component/Safe'
|
||||
import AppRoutes from '~/routes'
|
||||
import { WITHDRAW_BUTTON_TEXT } from '~/routes/safe/component/Safe/DailyLimit'
|
||||
import WithdrawComponent, { SEE_TXS_BUTTON_TEXT } from '~/routes/safe/component/Withdraw'
|
||||
import { getBalanceInEtherOf } from '~/wallets/getWeb3'
|
||||
import { sleep } from '~/utils/timer'
|
||||
import { getDailyLimitFrom } from '~/routes/safe/component/Withdraw/withdraw'
|
||||
import { type DailyLimitProps } from '~/routes/safe/store/model/dailyLimit'
|
||||
import { ADD_MULTISIG_BUTTON_TEXT } from '~/routes/safe/component/Safe/MultisigTx'
|
||||
import { WITHDRAW_INDEX, MOVE_FUNDS_INDEX } from '~/test/builder/safe.dom.utils'
|
||||
import { buildMathPropsFrom } from '~/test/utils/buildReactRouterProps'
|
||||
import { safeSelector } from '~/routes/safe/store/selectors/index'
|
||||
|
||||
describe('React DOM TESTS > Withdraw funds from safe', () => {
|
||||
let SafeDom
|
||||
let store
|
||||
let address
|
||||
beforeEach(async () => {
|
||||
// create store
|
||||
store = aNewStore()
|
||||
// deploy safe updating store
|
||||
address = await aDeployedSafe(store)
|
||||
// navigate to SAFE route
|
||||
history.push(`${SAFELIST_ADDRESS}/${address}`)
|
||||
SafeDom = TestUtils.renderIntoDocument((
|
||||
<Provider store={store}>
|
||||
<ConnectedRouter history={history}>
|
||||
<AppRoutes />
|
||||
</ConnectedRouter>
|
||||
</Provider>
|
||||
))
|
||||
})
|
||||
|
||||
it('should withdraw funds under dailyLimit without needing confirmations', async () => {
|
||||
// add funds to safe
|
||||
await addEtherTo(address, '0.1')
|
||||
await sleep(3000)
|
||||
const Safe = TestUtils.findRenderedComponentWithType(SafeDom, SafeView)
|
||||
|
||||
// $FlowFixMe
|
||||
const buttons = TestUtils.scryRenderedDOMComponentsWithTag(Safe, 'button')
|
||||
const addWithdrawButton = buttons[WITHDRAW_INDEX]
|
||||
expect(addWithdrawButton.getElementsByTagName('span')[0].innerHTML).toEqual(WITHDRAW_BUTTON_TEXT)
|
||||
TestUtils.Simulate.click(addWithdrawButton)
|
||||
await sleep(4000)
|
||||
const Withdraw = TestUtils.findRenderedComponentWithType(SafeDom, WithdrawComponent)
|
||||
|
||||
// $FlowFixMe
|
||||
const inputs = TestUtils.scryRenderedDOMComponentsWithTag(Withdraw, 'input')
|
||||
const amountInEth = inputs[0]
|
||||
const toAddress = inputs[1]
|
||||
TestUtils.Simulate.change(amountInEth, { target: { value: '0.01' } })
|
||||
TestUtils.Simulate.change(toAddress, { target: { value: store.getState().providers.account } })
|
||||
|
||||
// $FlowFixMe
|
||||
const form = TestUtils.findRenderedDOMComponentWithTag(Withdraw, 'form')
|
||||
|
||||
TestUtils.Simulate.submit(form) // fill the form
|
||||
TestUtils.Simulate.submit(form) // confirming data
|
||||
await sleep(6000)
|
||||
|
||||
const safeBalance = await getBalanceInEtherOf(address)
|
||||
expect(safeBalance).toBe('0.09')
|
||||
|
||||
// $FlowFixMe
|
||||
const withdrawButtons = TestUtils.scryRenderedComponentsWithType(Withdraw, Button)
|
||||
const visitTxsButton = withdrawButtons[0]
|
||||
expect(visitTxsButton.props.children).toEqual(SEE_TXS_BUTTON_TEXT)
|
||||
})
|
||||
|
||||
it('spentToday dailyLimitModule property is updated correctly', async () => {
|
||||
// add funds to safe
|
||||
await addEtherTo(address, '0.1')
|
||||
|
||||
const match: Match = buildMathPropsFrom(address)
|
||||
const safe = safeSelector(store.getState(), { match })
|
||||
await executeWithdrawOn(safe, 0.01)
|
||||
await executeWithdrawOn(safe, 0.01)
|
||||
|
||||
const ethAddress = 0
|
||||
const dailyLimit: DailyLimitProps = await getDailyLimitFrom(address, ethAddress)
|
||||
|
||||
// THEN
|
||||
expect(dailyLimit.value).toBe(0.5)
|
||||
expect(dailyLimit.spentToday).toBe(0.02)
|
||||
})
|
||||
|
||||
it('add multisig txs button disabled when balance is 0', async () => {
|
||||
const Safe = TestUtils.findRenderedComponentWithType(SafeDom, SafeView)
|
||||
// $FlowFixMe
|
||||
const buttons = TestUtils.scryRenderedDOMComponentsWithTag(Safe, 'button')
|
||||
const addTxButton = buttons[MOVE_FUNDS_INDEX]
|
||||
expect(addTxButton.getElementsByTagName('span')[0].innerHTML).toEqual(ADD_MULTISIG_BUTTON_TEXT)
|
||||
expect(addTxButton.hasAttribute('disabled')).toBe(true)
|
||||
|
||||
await addEtherTo(address, '0.1')
|
||||
await sleep(1800)
|
||||
|
||||
expect(addTxButton.hasAttribute('disabled')).toBe(false)
|
||||
})
|
||||
|
||||
it('Withdraw button disabled when balance is 0', async () => {
|
||||
const Safe = TestUtils.findRenderedComponentWithType(SafeDom, SafeView)
|
||||
// $FlowFixMe
|
||||
const buttons = TestUtils.scryRenderedDOMComponentsWithTag(Safe, 'button')
|
||||
const addWithdrawButton = buttons[WITHDRAW_INDEX]
|
||||
expect(addWithdrawButton.getElementsByTagName('span')[0].innerHTML).toEqual(WITHDRAW_BUTTON_TEXT)
|
||||
expect(addWithdrawButton.hasAttribute('disabled')).toBe(true)
|
||||
|
||||
await addEtherTo(address, '0.1')
|
||||
await sleep(1800)
|
||||
|
||||
expect(addWithdrawButton.hasAttribute('disabled')).toBe(false)
|
||||
})
|
||||
})
|
|
@ -1,114 +1,9 @@
|
|||
// @flow
|
||||
import TestUtils from 'react-dom/test-utils'
|
||||
import { sleep } from '~/utils/timer'
|
||||
import { getBalanceInEtherOf } from '~/wallets/getWeb3'
|
||||
import { ADD_MULTISIG_BUTTON_TEXT, SEE_MULTISIG_BUTTON_TEXT } from '~/routes/safe/component/Safe/MultisigTx'
|
||||
import { addEtherTo } from '~/test/utils/etherMovements'
|
||||
import SafeView from '~/routes/safe/component/Safe'
|
||||
import TransactionsComponent from '~/routes/safe/component/Transactions'
|
||||
import TransactionComponent from '~/routes/safe/component/Transactions/Transaction'
|
||||
import { safeTransactionsSelector } from '~/routes/safe/store/selectors/index'
|
||||
import { type GlobalState } from '~/store/index'
|
||||
import ListItemText from '~/components/List/ListItemText'
|
||||
import { MOVE_FUNDS_INDEX, LIST_TXS_INDEX } from '~/test/builder/safe.dom.utils'
|
||||
|
||||
export const createMultisigTxFilling = async (
|
||||
SafeDom: React$Component<any, any>,
|
||||
AddTransactionComponent: React$ElementType,
|
||||
store: Store<GlobalState>,
|
||||
) => {
|
||||
// Get AddTransaction form component
|
||||
const AddTransaction = TestUtils.findRenderedComponentWithType(SafeDom, AddTransactionComponent)
|
||||
|
||||
// $FlowFixMe
|
||||
const inputs = TestUtils.scryRenderedDOMComponentsWithTag(AddTransaction, 'input')
|
||||
const name = inputs[0]
|
||||
const destination = inputs[1]
|
||||
const amountInEth = inputs[2]
|
||||
TestUtils.Simulate.change(name, { target: { value: 'Buying betteries' } })
|
||||
TestUtils.Simulate.change(amountInEth, { target: { value: '0.01' } })
|
||||
TestUtils.Simulate.change(destination, { target: { value: store.getState().providers.account } })
|
||||
|
||||
// $FlowFixMe
|
||||
const form = TestUtils.findRenderedDOMComponentWithTag(AddTransaction, 'form')
|
||||
|
||||
TestUtils.Simulate.submit(form) // fill the form
|
||||
TestUtils.Simulate.submit(form) // confirming data
|
||||
return sleep(4000)
|
||||
}
|
||||
|
||||
export const checkBalanceOf = async (addressToTest: string, value: string) => {
|
||||
const safeBalance = await getBalanceInEtherOf(addressToTest)
|
||||
expect(safeBalance).toBe(value)
|
||||
}
|
||||
|
||||
export const addFundsTo = async (SafeDom: React$Component<any, any>, destination: string) => {
|
||||
// add funds to safe
|
||||
await addEtherTo(destination, '0.1')
|
||||
const Safe = TestUtils.findRenderedComponentWithType(SafeDom, SafeView)
|
||||
|
||||
// $FlowFixMe
|
||||
const buttons = TestUtils.scryRenderedDOMComponentsWithTag(Safe, 'button')
|
||||
const addTxButton = buttons[MOVE_FUNDS_INDEX]
|
||||
expect(addTxButton.getElementsByTagName('span')[0].innerHTML).toEqual(ADD_MULTISIG_BUTTON_TEXT)
|
||||
|
||||
await sleep(1800) // Give time to enable Add button
|
||||
TestUtils.Simulate.click(addTxButton)
|
||||
}
|
||||
|
||||
export const listTxsOf = (SafeDom: React$Component<any, any>) => {
|
||||
const Safe = TestUtils.findRenderedComponentWithType(SafeDom, SafeView)
|
||||
|
||||
// $FlowFixMe
|
||||
const buttons = TestUtils.scryRenderedDOMComponentsWithTag(Safe, 'button')
|
||||
const seeTx = buttons[LIST_TXS_INDEX]
|
||||
expect(seeTx.getElementsByTagName('span')[0].innerHTML).toEqual(SEE_MULTISIG_BUTTON_TEXT)
|
||||
TestUtils.Simulate.click(seeTx)
|
||||
}
|
||||
|
||||
export const getListItemsFrom = (SafeDom: React$Component<any, any>) => {
|
||||
const Transactions = TestUtils.findRenderedComponentWithType(SafeDom, TransactionsComponent)
|
||||
if (!Transactions) throw new Error()
|
||||
const Transaction = TestUtils.findRenderedComponentWithType(Transactions, TransactionComponent)
|
||||
if (!Transaction) throw new Error()
|
||||
|
||||
return TestUtils.scryRenderedComponentsWithType(Transaction, ListItemText)
|
||||
}
|
||||
|
||||
export const expandTransactionOf = async (
|
||||
SafeDom: React$Component<any, any>,
|
||||
numOwners: number,
|
||||
safeThreshold: number,
|
||||
) => {
|
||||
const listItems = getListItemsFrom(SafeDom)
|
||||
TestUtils.Simulate.click(TestUtils.scryRenderedDOMComponentsWithTag(listItems[2], 'p')[0]) // expanded
|
||||
await sleep(2500) // Time to expand
|
||||
const listItemsExpanded = getListItemsFrom(SafeDom)
|
||||
const threshold = listItemsExpanded[5]
|
||||
expect(threshold.props.secondary).toContain(`confirmation${safeThreshold === 1 ? '' : 's'} needed`)
|
||||
TestUtils.Simulate.click(TestUtils.scryRenderedDOMComponentsWithTag(threshold, 'p')[0]) // expanded
|
||||
await sleep(2500) // Time to expand
|
||||
expect(listItemsExpanded.length).toBe(listItems.length + numOwners)
|
||||
}
|
||||
|
||||
export const getTransactionFromReduxStore = (store: Store<GlobalState>, address: string, index: number = 0) => {
|
||||
const transactions = safeTransactionsSelector(store.getState(), { safeAddress: address })
|
||||
|
||||
return transactions.get(index)
|
||||
}
|
||||
|
||||
export const confirmOwners = async (SafeDom: React$Component<any, any>, ...statusses: string[]) => {
|
||||
const Transactions = TestUtils.findRenderedComponentWithType(SafeDom, TransactionsComponent)
|
||||
if (!Transactions) throw new Error()
|
||||
const Transaction = TestUtils.findRenderedComponentWithType(Transactions, TransactionComponent)
|
||||
if (!Transaction) throw new Error()
|
||||
|
||||
const listItems = TestUtils.scryRenderedComponentsWithType(Transaction, ListItemText)
|
||||
|
||||
for (let i = 0; i < statusses.length; i += 1) {
|
||||
const ownerIndex = i + 6
|
||||
const ownerParagraph = listItems[ownerIndex].props.primary
|
||||
|
||||
expect(statusses[i]).toEqual(ownerParagraph)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ const safesListSelectorTests = () => {
|
|||
const reduxStore = {
|
||||
[PROVIDER_REDUCER_ID]: walletRecord,
|
||||
[SAFE_REDUCER_ID]: Map(),
|
||||
balances: undefined,
|
||||
tokens: undefined,
|
||||
transactions: undefined,
|
||||
}
|
||||
const emptyList = List([])
|
||||
|
@ -42,7 +42,7 @@ const safesListSelectorTests = () => {
|
|||
const reduxStore = {
|
||||
[PROVIDER_REDUCER_ID]: walletRecord,
|
||||
[SAFE_REDUCER_ID]: map,
|
||||
balances: undefined,
|
||||
tokens: undefined,
|
||||
transactions: undefined,
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ const safesListSelectorTests = () => {
|
|||
const reduxStore = {
|
||||
[PROVIDER_REDUCER_ID]: walletRecord,
|
||||
[SAFE_REDUCER_ID]: map,
|
||||
balances: undefined,
|
||||
tokens: undefined,
|
||||
transactions: undefined,
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ const safesListSelectorTests = () => {
|
|||
const reduxStore = {
|
||||
[SAFE_REDUCER_ID]: map,
|
||||
[PROVIDER_REDUCER_ID]: walletRecord,
|
||||
balances: undefined,
|
||||
tokens: undefined,
|
||||
transactions: undefined,
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ const safesListSelectorTests = () => {
|
|||
const reduxStore = {
|
||||
[SAFE_REDUCER_ID]: map,
|
||||
[PROVIDER_REDUCER_ID]: walletRecord,
|
||||
balances: undefined,
|
||||
tokens: undefined,
|
||||
transactions: undefined,
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
// @flow
|
||||
import { storiesOf } from '@storybook/react'
|
||||
import * as React from 'react'
|
||||
import { List } from 'immutable'
|
||||
import styles from '~/components/layout/PageFrame/index.scss'
|
||||
import AddTokenForm from './index'
|
||||
|
||||
const FrameDecorator = story => (
|
||||
<div className={styles.frame} style={{ textAlign: 'center' }}>
|
||||
{ story() }
|
||||
</div>
|
||||
)
|
||||
storiesOf('Components', module)
|
||||
.addDecorator(FrameDecorator)
|
||||
.add('AddTokenForm', () => (
|
||||
// $FlowFixMe
|
||||
<AddTokenForm tokens={List([]).toArray()} safeAddress="" />
|
||||
))
|
|
@ -0,0 +1,56 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import Field from '~/components/forms/Field'
|
||||
import TextField from '~/components/forms/TextField'
|
||||
import { composeValidators, required, mustBeEthereumAddress, uniqueAddress } from '~/components/forms/validator'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Heading from '~/components/layout/Heading'
|
||||
import { promisify } from '~/utils/promisify'
|
||||
import { getWeb3 } from '~/wallets/getWeb3'
|
||||
import { EMPTY_DATA } from '~/wallets/ethTransactions'
|
||||
import { getStandardTokenContract } from '~/routes/tokens/store/actions/fetchTokens'
|
||||
|
||||
type Props = {
|
||||
addresses: string[],
|
||||
}
|
||||
|
||||
export const TOKEN_ADRESS_PARAM = 'tokenAddress'
|
||||
|
||||
export const token = async (tokenAddress: string) => {
|
||||
const code = await promisify(cb => getWeb3().eth.getCode(tokenAddress, cb))
|
||||
const isDeployed = code !== EMPTY_DATA
|
||||
|
||||
if (!isDeployed) {
|
||||
return 'Specified address is not deployed on the current network'
|
||||
}
|
||||
|
||||
const erc20Token = await getStandardTokenContract()
|
||||
const instance = await erc20Token.at(tokenAddress)
|
||||
const supply = await instance.totalSupply()
|
||||
|
||||
if (Number(supply) === 0) {
|
||||
return 'Specified address is not a valid standard token'
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
const FirstPage = ({ addresses }: Props) => () => (
|
||||
<Block margin="md">
|
||||
<Heading tag="h2" margin="lg">
|
||||
Add Custom ERC20 Token
|
||||
</Heading>
|
||||
<Block margin="md">
|
||||
<Field
|
||||
name={TOKEN_ADRESS_PARAM}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={composeValidators(required, mustBeEthereumAddress, uniqueAddress(addresses), token)}
|
||||
placeholder="ERC20 Token Address*"
|
||||
text="ERC20 Token Address"
|
||||
/>
|
||||
</Block>
|
||||
</Block>
|
||||
)
|
||||
|
||||
export default FirstPage
|
|
@ -0,0 +1,45 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import CircularProgress from '@material-ui/core/CircularProgress'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Bold from '~/components/layout/Bold'
|
||||
import Heading from '~/components/layout/Heading'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import { TOKEN_ADRESS_PARAM } from '~/routes/tokens/component/AddToken/FirstPage'
|
||||
import { TOKEN_LOGO_URL_PARAM, TOKEN_NAME_PARAM, TOKEN_SYMBOL_PARAM, TOKEN_DECIMALS_PARAM } from '~/routes/tokens/component/AddToken/SecondPage'
|
||||
|
||||
type FormProps = {
|
||||
values: Object,
|
||||
submitting: boolean,
|
||||
}
|
||||
|
||||
const spinnerStyle = {
|
||||
minHeight: '50px',
|
||||
}
|
||||
|
||||
const Review = () => ({ values, submitting }: FormProps) => (
|
||||
<Block>
|
||||
<Heading tag="h2">Review ERC20 Token operation</Heading>
|
||||
<Paragraph align="left">
|
||||
<Bold>Token address: </Bold> {values[TOKEN_ADRESS_PARAM]}
|
||||
</Paragraph>
|
||||
<Paragraph align="left">
|
||||
<Bold>Token name: </Bold> {values[TOKEN_NAME_PARAM]}
|
||||
</Paragraph>
|
||||
<Paragraph align="left">
|
||||
<Bold>Token symbol: </Bold> {values[TOKEN_SYMBOL_PARAM]}
|
||||
</Paragraph>
|
||||
<Paragraph align="left">
|
||||
<Bold>Token decimals: </Bold> {values[TOKEN_DECIMALS_PARAM]}
|
||||
</Paragraph>
|
||||
<Paragraph align="left">
|
||||
<Bold>Token logo: </Bold> {values[TOKEN_LOGO_URL_PARAM]}
|
||||
</Paragraph>
|
||||
<Block style={spinnerStyle}>
|
||||
{ submitting && <CircularProgress size={50} /> }
|
||||
</Block>
|
||||
</Block>
|
||||
)
|
||||
|
||||
|
||||
export default Review
|
|
@ -0,0 +1,62 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import Field from '~/components/forms/Field'
|
||||
import TextField from '~/components/forms/TextField'
|
||||
import { composeValidators, required, mustBeInteger, mustBeUrl } from '~/components/forms/validator'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Heading from '~/components/layout/Heading'
|
||||
|
||||
export const TOKEN_NAME_PARAM = 'tokenName'
|
||||
export const TOKEN_SYMBOL_PARAM = 'tokenSymbol'
|
||||
export const TOKEN_DECIMALS_PARAM = 'tokenDecimals'
|
||||
export const TOKEN_LOGO_URL_PARAM = 'tokenLogo'
|
||||
|
||||
const SecondPage = () => () => (
|
||||
<Block margin="md">
|
||||
<Heading tag="h2" margin="lg">
|
||||
Complete Custom Token information
|
||||
</Heading>
|
||||
<Block margin="md">
|
||||
<Field
|
||||
name={TOKEN_NAME_PARAM}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={required}
|
||||
placeholder="ERC20 Token Name*"
|
||||
text="ERC20 Token Name"
|
||||
/>
|
||||
</Block>
|
||||
<Block margin="md">
|
||||
<Field
|
||||
name={TOKEN_SYMBOL_PARAM}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={required}
|
||||
placeholder="ERC20 Token Symbol*"
|
||||
text="ERC20 Token Symbol"
|
||||
/>
|
||||
</Block>
|
||||
<Block margin="md">
|
||||
<Field
|
||||
name={TOKEN_DECIMALS_PARAM}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={composeValidators(required, mustBeInteger)}
|
||||
placeholder="ERC20 Token Decimals*"
|
||||
text="ERC20 Token Decimals"
|
||||
/>
|
||||
</Block>
|
||||
<Block margin="md">
|
||||
<Field
|
||||
name={TOKEN_LOGO_URL_PARAM}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={composeValidators(required, mustBeUrl)}
|
||||
placeholder="ERC20 Token Logo url*"
|
||||
text="ERC20 Token Logo"
|
||||
/>
|
||||
</Block>
|
||||
</Block>
|
||||
)
|
||||
|
||||
export default SecondPage
|
|
@ -0,0 +1,128 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import Stepper from '~/components/Stepper'
|
||||
import { getHumanFriendlyToken } from '~/routes/tokens/store/actions/fetchTokens'
|
||||
import FirstPage, { TOKEN_ADRESS_PARAM } from '~/routes/tokens/component/AddToken/FirstPage'
|
||||
import SecondPage, { TOKEN_SYMBOL_PARAM, TOKEN_DECIMALS_PARAM, TOKEN_LOGO_URL_PARAM, TOKEN_NAME_PARAM } from '~/routes/tokens/component/AddToken/SecondPage'
|
||||
import { makeToken, type Token } from '~/routes/tokens/store/model/token'
|
||||
import addTokenAction from '~/routes/tokens/store/actions/addToken'
|
||||
import { getWeb3 } from '~/wallets/getWeb3'
|
||||
import { promisify } from '~/utils/promisify'
|
||||
import { EMPTY_DATA } from '~/wallets/ethTransactions'
|
||||
import Review from './Review'
|
||||
|
||||
export const getSteps = () => [
|
||||
'Fill Add Token Form', 'Check optional attributes', 'Review Information',
|
||||
]
|
||||
|
||||
type Props = {
|
||||
tokens: string[],
|
||||
safeAddress: string,
|
||||
addToken: typeof addTokenAction,
|
||||
}
|
||||
|
||||
type State = {
|
||||
done: boolean,
|
||||
}
|
||||
|
||||
export const ADD_TOKEN_RESET_BUTTON_TEXT = 'RESET'
|
||||
|
||||
export const addTokenFnc = async (values: Object, addToken: typeof addTokenAction, safeAddress: string) => {
|
||||
const address = values[TOKEN_ADRESS_PARAM]
|
||||
const name = values[TOKEN_NAME_PARAM]
|
||||
const symbol = values[TOKEN_SYMBOL_PARAM]
|
||||
const decimals = values[TOKEN_DECIMALS_PARAM]
|
||||
const logo = values[TOKEN_LOGO_URL_PARAM]
|
||||
|
||||
const token: Token = makeToken({
|
||||
address,
|
||||
name,
|
||||
symbol,
|
||||
decimals: Number(decimals),
|
||||
logoUrl: logo,
|
||||
status: true,
|
||||
removable: true,
|
||||
})
|
||||
|
||||
return addToken(safeAddress, token)
|
||||
}
|
||||
|
||||
class AddToken extends React.Component<Props, State> {
|
||||
state = {
|
||||
done: false,
|
||||
}
|
||||
|
||||
onAddToken = async (values: Object) => {
|
||||
const { addToken, safeAddress } = this.props
|
||||
|
||||
const result = addTokenFnc(values, addToken, safeAddress)
|
||||
this.setState({ done: true })
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
onReset = () => {
|
||||
this.setState({ done: false })
|
||||
}
|
||||
|
||||
fetchInitialPropsSecondPage = async (values: Object) => {
|
||||
const tokenAddress = values[TOKEN_ADRESS_PARAM]
|
||||
const erc20Token = await getHumanFriendlyToken()
|
||||
const instance = await erc20Token.at(tokenAddress)
|
||||
|
||||
const dataName = await instance.contract.name.getData()
|
||||
const nameResult = await promisify(cb => getWeb3().eth.call({ to: tokenAddress, data: dataName }, cb))
|
||||
const hasName = nameResult !== EMPTY_DATA
|
||||
|
||||
const dataSymbol = await instance.contract.symbol.getData()
|
||||
const symbolResult = await promisify(cb => getWeb3().eth.call({ to: tokenAddress, data: dataSymbol }, cb))
|
||||
const hasSymbol = symbolResult !== EMPTY_DATA
|
||||
|
||||
const dataDecimals = await instance.contract.decimals.getData()
|
||||
const decimalsResult = await promisify(cb => getWeb3().eth.call({ to: tokenAddress, data: dataDecimals }, cb))
|
||||
const hasDecimals = decimalsResult !== EMPTY_DATA
|
||||
|
||||
|
||||
const name = hasName ? await instance.name() : undefined
|
||||
const symbol = hasSymbol ? await instance.symbol() : undefined
|
||||
const decimals = hasDecimals ? `${await instance.decimals()}` : undefined
|
||||
|
||||
return ({
|
||||
[TOKEN_SYMBOL_PARAM]: symbol,
|
||||
[TOKEN_DECIMALS_PARAM]: decimals,
|
||||
[TOKEN_NAME_PARAM]: name,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { tokens, safeAddress } = this.props
|
||||
const { done } = this.state
|
||||
const steps = getSteps()
|
||||
const finishedButton = <Stepper.FinishButton title={ADD_TOKEN_RESET_BUTTON_TEXT} />
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Stepper
|
||||
finishedTransaction={done}
|
||||
finishedButton={finishedButton}
|
||||
onSubmit={this.onAddToken}
|
||||
steps={steps}
|
||||
onReset={this.onReset}
|
||||
disabledWhenValidating
|
||||
>
|
||||
<Stepper.Page addresses={tokens} prepareNextInitialProps={this.fetchInitialPropsSecondPage}>
|
||||
{ FirstPage }
|
||||
</Stepper.Page>
|
||||
<Stepper.Page safeAddress={safeAddress}>
|
||||
{ SecondPage }
|
||||
</Stepper.Page>
|
||||
<Stepper.Page>
|
||||
{ Review }
|
||||
</Stepper.Page>
|
||||
</Stepper>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default AddToken
|
|
@ -0,0 +1,126 @@
|
|||
// @flow
|
||||
import MuiList from '@material-ui/core/List'
|
||||
import * as React from 'react'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Col from '~/components/layout/Col'
|
||||
import AccountBalanceWallet from '@material-ui/icons/AccountBalanceWallet'
|
||||
import AddCircle from '@material-ui/icons/AddCircle'
|
||||
import Link from '~/components/layout/Link'
|
||||
import Bold from '~/components/layout/Bold'
|
||||
import Img from '~/components/layout/Img'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import Row from '~/components/layout/Row'
|
||||
import { type Token } from '~/routes/tokens/store/model/token'
|
||||
import { type SelectorProps } from '~/routes/tokens/container/selector'
|
||||
import { type Actions } from '~/routes/tokens/container/actions'
|
||||
import { SAFELIST_ADDRESS } from '~/routes/routes'
|
||||
import AddToken from '~/routes/tokens/component/AddToken'
|
||||
import RemoveToken from '~/routes/tokens/component/RemoveToken'
|
||||
import TokenComponent from './Token'
|
||||
|
||||
const safeIcon = require('~/routes/safe/component/Safe/assets/gnosis_safe.svg')
|
||||
|
||||
type TokenProps = SelectorProps & Actions
|
||||
|
||||
type State = {
|
||||
component: React$Node,
|
||||
}
|
||||
|
||||
const listStyle = {
|
||||
width: '100%',
|
||||
paddingBottom: 0,
|
||||
}
|
||||
|
||||
class TokenLayout extends React.PureComponent<TokenProps, State> {
|
||||
state = {
|
||||
component: undefined,
|
||||
}
|
||||
|
||||
onAddToken = () => {
|
||||
const { addresses, safeAddress, addToken } = this.props
|
||||
|
||||
this.setState({
|
||||
component: <AddToken
|
||||
addToken={addToken}
|
||||
tokens={addresses.toArray()}
|
||||
safeAddress={safeAddress}
|
||||
/>,
|
||||
})
|
||||
}
|
||||
|
||||
onReset = () => {
|
||||
this.setState({ component: undefined })
|
||||
}
|
||||
|
||||
onRemoveToken = (token: Token) => {
|
||||
const { safeAddress, removeToken } = this.props
|
||||
|
||||
this.setState({
|
||||
component: <RemoveToken
|
||||
token={token}
|
||||
safeAddress={safeAddress}
|
||||
removeTokenAction={removeToken}
|
||||
onReset={this.onReset}
|
||||
/>,
|
||||
})
|
||||
}
|
||||
|
||||
onEnableToken = (token: Token) => {
|
||||
const { enableToken, safe } = this.props
|
||||
const safeAddress = safe.get('address')
|
||||
|
||||
enableToken(safeAddress, token)
|
||||
}
|
||||
|
||||
onDisableToken = (token: Token) => {
|
||||
const { disableToken, safe } = this.props
|
||||
const safeAddress = safe.get('address')
|
||||
|
||||
disableToken(safeAddress, token)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { safe, safeAddress, tokens } = this.props
|
||||
const { component } = this.state
|
||||
const name = safe ? safe.get('name') : ''
|
||||
|
||||
return (
|
||||
<Row grow>
|
||||
<Col sm={12} top="xs" md={5} margin="xl" overflow>
|
||||
<MuiList style={listStyle}>
|
||||
{tokens.map((token: Token) => (
|
||||
<TokenComponent
|
||||
key={token.get('address')}
|
||||
token={token}
|
||||
onDisableToken={this.onDisableToken}
|
||||
onEnableToken={this.onEnableToken}
|
||||
onRemove={this.onRemoveToken}
|
||||
/>
|
||||
))}
|
||||
</MuiList>
|
||||
</Col>
|
||||
<Col sm={12} center="xs" md={7} margin="xl" layout="column">
|
||||
<Block margin="xl">
|
||||
<Paragraph size="lg" noMargin align="right">
|
||||
<IconButton to={`${SAFELIST_ADDRESS}/${safeAddress}`} component={Link}>
|
||||
<AccountBalanceWallet />
|
||||
</IconButton>
|
||||
<IconButton onClick={this.onAddToken}>
|
||||
<AddCircle />
|
||||
</IconButton>
|
||||
<Bold>{name}</Bold>
|
||||
</Paragraph>
|
||||
</Block>
|
||||
<Row grow>
|
||||
<Col sm={12} center={component ? undefined : 'sm'} middle={component ? undefined : 'sm'} layout="column">
|
||||
{ component || <Img alt="Safe Icon" src={safeIcon} height={330} /> }
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default TokenLayout
|
|
@ -0,0 +1,38 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import CircularProgress from '@material-ui/core/CircularProgress'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Bold from '~/components/layout/Bold'
|
||||
import Heading from '~/components/layout/Heading'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
|
||||
type Props = {
|
||||
name: string,
|
||||
funds: string,
|
||||
symbol: string,
|
||||
}
|
||||
|
||||
type FormProps = {
|
||||
submitting: boolean,
|
||||
}
|
||||
|
||||
const spinnerStyle = {
|
||||
minHeight: '50px',
|
||||
}
|
||||
|
||||
const Review = ({ name, funds, symbol }: Props) => ({ submitting }: FormProps) => (
|
||||
<Block>
|
||||
<Heading tag="h2">Remove CUSTOM ERC 20 Token</Heading>
|
||||
<Paragraph align="left">
|
||||
<Bold>You are about to remove the custom token: </Bold> {name}
|
||||
</Paragraph>
|
||||
<Paragraph align="left">
|
||||
<Bold>{`You have ${funds} ${symbol} in your wallet`}</Bold>
|
||||
</Paragraph>
|
||||
<Block style={spinnerStyle}>
|
||||
{ submitting && <CircularProgress size={50} /> }
|
||||
</Block>
|
||||
</Block>
|
||||
)
|
||||
|
||||
export default Review
|
|
@ -0,0 +1,73 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import { type Token } from '~/routes/tokens/store/model/token'
|
||||
import Stepper from '~/components/Stepper'
|
||||
import RemoveTokenAction from '~/routes/tokens/store/actions/removeToken'
|
||||
import Review from '~/routes/tokens/component/RemoveToken/Review'
|
||||
|
||||
const getSteps = () => [
|
||||
'Review remove token operation',
|
||||
]
|
||||
|
||||
type Props = {
|
||||
token: Token,
|
||||
safeAddress: string,
|
||||
removeTokenAction: typeof RemoveTokenAction,
|
||||
onReset: () => void,
|
||||
}
|
||||
|
||||
type State = {
|
||||
done: boolean,
|
||||
}
|
||||
|
||||
export const REMOVE_TOKEN_RESET_BUTTON_TEXT = 'RESET'
|
||||
|
||||
export const removeToken = async (safeAddress: string, token: Token, removeTokenAction: typeof RemoveTokenAction) =>
|
||||
removeTokenAction(safeAddress, token)
|
||||
|
||||
class RemoveToken extends React.PureComponent<Props, State> {
|
||||
state = {
|
||||
done: false,
|
||||
}
|
||||
|
||||
onRemoveReset = () => {
|
||||
this.setState({ done: false }, this.props.onReset())
|
||||
}
|
||||
|
||||
executeRemoveOperation = async () => {
|
||||
try {
|
||||
const { token, safeAddress, removeTokenAction } = this.props
|
||||
await removeToken(safeAddress, token, removeTokenAction)
|
||||
this.setState({ done: true })
|
||||
} catch (error) {
|
||||
this.setState({ done: false })
|
||||
// eslint-disable-next-line
|
||||
console.log('Error while removing owner ' + error)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { done } = this.state
|
||||
const { token } = this.props
|
||||
const finishedButton = <Stepper.FinishButton title={REMOVE_TOKEN_RESET_BUTTON_TEXT} />
|
||||
const steps = getSteps()
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Stepper
|
||||
finishedTransaction={done}
|
||||
finishedButton={finishedButton}
|
||||
onSubmit={this.executeRemoveOperation}
|
||||
steps={steps}
|
||||
onReset={this.onRemoveReset}
|
||||
>
|
||||
<Stepper.Page name={token.get('name')} symbol={token.get('symbol')} funds={token.get('funds')}>
|
||||
{ Review }
|
||||
</Stepper.Page>
|
||||
</Stepper>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default RemoveToken
|
|
@ -0,0 +1,96 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import { type Token } from '~/routes/tokens/store/model/token'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Checkbox from '@material-ui/core/Checkbox'
|
||||
import Card from '@material-ui/core/Card'
|
||||
import CardContent from '@material-ui/core/CardContent'
|
||||
import CardMedia from '@material-ui/core/CardMedia'
|
||||
import Typography from '@material-ui/core/Typography'
|
||||
import { isEther } from '~/utils/tokens'
|
||||
import Delete from '@material-ui/icons/Delete'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import { type WithStyles } from '~/theme/mui'
|
||||
|
||||
type Props = WithStyles & {
|
||||
token: Token,
|
||||
onRemove: (token: Token)=> void,
|
||||
onEnableToken: (token: Token) => void,
|
||||
onDisableToken: (token: Token) => void,
|
||||
}
|
||||
|
||||
type State = {
|
||||
checked: boolean,
|
||||
}
|
||||
|
||||
const styles = () => ({
|
||||
card: {
|
||||
display: 'flex',
|
||||
},
|
||||
details: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
content: {
|
||||
flex: '1 0 auto',
|
||||
},
|
||||
cover: {
|
||||
width: 150,
|
||||
margin: 10,
|
||||
backgroundSize: '50%',
|
||||
},
|
||||
})
|
||||
|
||||
class TokenComponent extends React.PureComponent<Props, State> {
|
||||
state = {
|
||||
checked: this.props.token.get('status'),
|
||||
}
|
||||
|
||||
onRemoveClick = () => this.props.onRemove(this.props.token)
|
||||
|
||||
handleChange = (e: SyntheticInputEvent<HTMLInputElement>) => {
|
||||
const { checked } = e.target
|
||||
|
||||
const callback = checked ? this.props.onEnableToken : this.props.onDisableToken
|
||||
this.setState(() => ({ checked }), () => callback(this.props.token))
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes, token } = this.props
|
||||
const name = token.get('name')
|
||||
const symbol = token.get('symbol')
|
||||
const disabled = isEther(symbol)
|
||||
|
||||
return (
|
||||
<Card className={classes.card}>
|
||||
<Block className={classes.details}>
|
||||
<CardContent className={classes.content}>
|
||||
<Typography variant="headline">{name}</Typography>
|
||||
<Typography variant="subheading" color="textSecondary">
|
||||
<Checkbox
|
||||
disabled={disabled}
|
||||
checked={!!this.state.checked}
|
||||
onChange={this.handleChange}
|
||||
color="primary"
|
||||
/>
|
||||
{symbol}
|
||||
{ token.get('removable') &&
|
||||
<IconButton aria-label="Delete" onClick={this.onRemoveClick}>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Block>
|
||||
<CardMedia
|
||||
className={classes.cover}
|
||||
image={token.get('logoUrl')}
|
||||
title={name}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles, { withTheme: true })(TokenComponent)
|
|
@ -0,0 +1,21 @@
|
|||
// @flow
|
||||
import addToken from '~/routes/tokens/store/actions/addToken'
|
||||
import removeToken from '~/routes/tokens/store/actions/removeToken'
|
||||
import enableToken from '~/routes/tokens/store/actions/enableToken'
|
||||
import disableToken from '~/routes/tokens/store/actions/disableToken'
|
||||
import { fetchTokens } from '~/routes/tokens/store/actions/fetchTokens'
|
||||
|
||||
export type Actions = {
|
||||
enableToken: typeof enableToken,
|
||||
disableToken: typeof disableToken,
|
||||
addToken: typeof addToken,
|
||||
removeToken: typeof removeToken,
|
||||
}
|
||||
|
||||
export default {
|
||||
addToken,
|
||||
removeToken,
|
||||
enableToken,
|
||||
disableToken,
|
||||
fetchTokens,
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import Page from '~/components/layout/Page'
|
||||
import Layout from '~/routes/tokens/component/Layout'
|
||||
import { fetchTokens } from '~/routes/tokens/store/actions/fetchTokens'
|
||||
import selector, { type SelectorProps } from './selector'
|
||||
import actions, { type Actions } from './actions'
|
||||
|
||||
type Props = Actions & SelectorProps & {
|
||||
fetchTokens: typeof fetchTokens,
|
||||
}
|
||||
|
||||
class TokensView extends React.PureComponent<Props> {
|
||||
componentDidUpdate() {
|
||||
const { safeAddress } = this.props
|
||||
|
||||
if (this.props.tokens.count() === 0) {
|
||||
this.props.fetchTokens(safeAddress)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
tokens, addresses, safe, safeAddress, disableToken, enableToken, addToken, removeToken,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<Layout
|
||||
tokens={tokens}
|
||||
addresses={addresses}
|
||||
safe={safe}
|
||||
safeAddress={safeAddress}
|
||||
disableToken={disableToken}
|
||||
enableToken={enableToken}
|
||||
addToken={addToken}
|
||||
removeToken={removeToken}
|
||||
/>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(selector, actions)(TokensView)
|
|
@ -0,0 +1,21 @@
|
|||
// @flow
|
||||
import { List } from 'immutable'
|
||||
import { createStructuredSelector } from 'reselect'
|
||||
import { tokenListSelector, tokenAddressesSelector } from '~/routes/tokens/store/selectors'
|
||||
import { type Safe } from '~/routes/safe/store/model/safe'
|
||||
import { safeSelector, safeParamAddressSelector } from '~/routes/safe/store/selectors'
|
||||
import { type Token } from '~/routes/tokens/store/model/token'
|
||||
|
||||
export type SelectorProps = {
|
||||
tokens: List<Token>,
|
||||
addresses: List<string>,
|
||||
safe: Safe,
|
||||
safeAddress: string,
|
||||
}
|
||||
|
||||
export default createStructuredSelector({
|
||||
safe: safeSelector,
|
||||
safeAddress: safeParamAddressSelector,
|
||||
tokens: tokenListSelector,
|
||||
addresses: tokenAddressesSelector,
|
||||
})
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue