Merge pull request #48 from gnosis/feature/WA-232-display-token-funds

WA-232 - Feature display token funds
This commit is contained in:
Adolfo Panizo 2018-07-05 16:22:02 +02:00 committed by GitHub
commit 5e8cbff632
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
72 changed files with 22722 additions and 17369 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,16 @@
"4": {
"events": {},
"links": {},
"address": "0xe94c33a523bf201e412f601eff906eec0b1d21f5",
"transactionHash": "0x1f04811d6fb97329f6e96a579587fe5a3abe9fe2f202d4b2e27adb78d3de1c48"
"address": "0x7115893bc1477bc22f127bc2d82fcb4dd99e5838",
"transactionHash": "0x1f321cf880f5ec5b189cb1ac1c1c1b71ef8d854fc059c345fea6e6129bb19645"
},
"1529327661534": {
"1530013596495": {
"events": {},
"links": {},
"address": "0xa2a1eb294e1191c1e374f9015a37db35c49e9f26",
"transactionHash": "0x5cd2551fce05a8b864a085616ee54449fdddd56dcdebf1019bebd0839c69ef5f"
},
"1529333415702": {
"events": {},
"links": {},
"address": "0xaba1a0da223a2a5e04158bd80b2af7671e27e2c6",
"transactionHash": "0x6e7c0c3947a3a851738677ae63915781ce79fdfb0453030fc63bbcd346f2cf89"
},
"1529652204341": {
"events": {},
"links": {},
"address": "0x2013688de17569d52fc4cd4fe5f18821d8b32b2a",
"transactionHash": "0x2d7f72a94ced345c372040faa1f51a51f3e8da27a02dd04b9eefef487de3ba05"
"address": "0x18a8eaa498a58752858e5a912c8ff136b0bc6c69",
"transactionHash": "0x7da393438ee13b991e6d94278addb64e86b0c13101efb8a0f0f00d9f86ba51d0"
}
},
"schemaVersion": "2.0.0",
"updatedAt": "2018-06-22T07:24:16.274Z"
"updatedAt": "2018-06-29T10:54:03.255Z"
}

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

View File

@ -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"
}

View File

@ -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

View File

@ -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

View File

@ -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'
@ -21,7 +20,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 = {

View File

@ -3,12 +3,14 @@ import * as React from 'react'
import { type FormApi } from 'final-form'
import { Form } 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) => ?Object
) => ?Object | Promise<?Object> | void,
onSubmit: OnSubmit,
children: Function,
padding: number,
validation?: (values: Object) => Object | Promise<Object>,

View File

@ -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')
@ -48,7 +49,7 @@ 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) => {

View File

@ -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 = ({

View File

@ -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 = {

View File

@ -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>
))

View File

@ -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()

View File

@ -7,11 +7,11 @@ import GnoSafe from './Safe'
type Props = SelectorProps
const Layout = ({
safe, balance, provider, userAddress,
safe, balances, provider, userAddress,
}: Props) => (
<React.Fragment>
{ safe
? <GnoSafe safe={safe} balance={balance} userAddress={userAddress} />
? <GnoSafe safe={safe} balances={balances} userAddress={userAddress} />
: <NoSafe provider={provider} text="Not found safe" />
}
</React.Fragment>

View File

@ -1,8 +1,10 @@
// @flow
import { storiesOf } from '@storybook/react'
import * as React from 'react'
import { Map } from 'immutable'
import styles from '~/components/layout/PageFrame/index.scss'
import { SafeFactory } from '~/routes/safe/store/test/builder/safe.builder'
import { makeBalance } from '~/routes/safe/store/model/balance'
import Component from './Layout'
@ -12,6 +14,15 @@ const FrameDecorator = story => (
</div>
)
const ethBalance = makeBalance({
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"
balances={Map()}
fetchBalance={() => {}}
/>
))
@ -28,7 +39,7 @@ storiesOf('Routes /safe:address', module)
userAddress="foo"
safe={undefined}
provider=""
balance="0"
balances={Map()}
fetchBalance={() => {}}
/>
))
@ -40,7 +51,7 @@ storiesOf('Routes /safe:address', module)
userAddress="foo"
safe={safe}
provider="METAMASK"
balance="2"
balances={Map().set('ETH', ethBalance)}
fetchBalance={() => {}}
/>
)
@ -53,7 +64,7 @@ storiesOf('Routes /safe:address', module)
userAddress="foo"
safe={safe}
provider="METAMASK"
balance="2"
balances={Map().set('ETH', ethBalance)}
fetchBalance={() => {}}
/>
)

View File

@ -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

View File

@ -0,0 +1,66 @@
// @flow
import * as React from 'react'
import AccountBalance from '@material-ui/icons/AccountBalance'
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 openHoc, { type Open } from '~/components/hoc/OpenHoc'
import { type WithStyles } from '~/theme/mui'
import { type Balance } from '~/routes/safe/store/model/balance'
type Props = Open & WithStyles & {
balances: Map<string, Balance>,
}
const styles = {
nested: {
paddingLeft: '40px',
},
}
const BalanceComponent = openHoc(({
open, toggle, balances, classes,
}: Props) => (
<React.Fragment>
<ListItem onClick={toggle}>
<Avatar>
<AccountBalance />
</Avatar>
<ListItemText primary="Balance" secondary="List of different token balances" />
<ListItemIcon>
{open
? <IconButton disableRipple><ExpandLess /></IconButton>
: <IconButton disableRipple><ExpandMore /></IconButton>
}
</ListItemIcon>
</ListItem>
<Collapse in={open} timeout="auto">
<List component="div" disablePadding>
{balances.valueSeq().map((balance: Balance) => {
const symbol = balance.get('symbol')
const name = balance.get('name')
return (
<ListItem key={symbol} className={classes.nested}>
<ListItemIcon>
<Img src={balance.get('logoUrl')} height={30} alt={name} />
</ListItemIcon>
<ListItemText primary={name} secondary={`${balance.get('funds')} ${symbol}`} />
</ListItem>
)
})}
</List>
</Collapse>
</React.Fragment>
))
export default withStyles(styles)(BalanceComponent)

View File

@ -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 (

View File

@ -7,7 +7,7 @@ import Button from '~/components/layout/Button'
import ListItemText from '~/components/List/ListItemText'
type Props = {
balance: string,
balance: number,
onAddTx: () => void,
onSeeTxs: () => void,
}
@ -21,7 +21,7 @@ const addStyle = {
const DailyLimitComponent = ({ balance, onAddTx, onSeeTxs }: Props) => {
const text = `Available ${balance} ETH`
const disabled = Number(balance) <= 0
const disabled = balance <= 0
return (
<ListItem>

View File

@ -1,5 +1,7 @@
// @flow
import List from '@material-ui/core/List'
import * as React from 'react'
import { Map } from 'immutable'
import Block from '~/components/layout/Block'
import Col from '~/components/layout/Col'
import Bold from '~/components/layout/Bold'
@ -7,7 +9,7 @@ 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 Balance } from '~/routes/safe/store/model/balance'
import Withdraw from '~/routes/safe/component/Withdraw'
import Transactions from '~/routes/safe/component/Transactions'
@ -18,7 +20,7 @@ import RemoveOwner from '~/routes/safe/component/RemoveOwner'
import EditDailyLimit from '~/routes/safe/component/EditDailyLimit'
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,
balances: Map<string, Balance>,
userAddress: string,
}
@ -40,6 +42,15 @@ const listStyle = {
width: '100%',
}
const getEthBalanceFrom = (balances: Map<string, Balance>) => {
const ethBalance = balances.get('ETH')
if (!ethBalance) {
return 0
}
return Number(ethBalance.get('funds'))
}
class GnoSafe extends React.PureComponent<SafeProps, State> {
state = {
component: undefined,
@ -59,9 +70,11 @@ class GnoSafe extends React.PureComponent<SafeProps, State> {
}
onAddTx = () => {
const { balance, safe } = this.props
const { balances, safe } = this.props
const ethBalance = getEthBalanceFrom(balances)
this.setState({
component: <AddTransaction safe={safe} balance={Number(balance)} onReset={this.onListTransactions} />,
component: <AddTransaction safe={safe} balance={Number(ethBalance)} onReset={this.onListTransactions} />,
})
}
@ -90,14 +103,15 @@ class GnoSafe extends React.PureComponent<SafeProps, State> {
}
render() {
const { safe, balance, userAddress } = this.props
const { safe, balances, userAddress } = this.props
const { component } = this.state
const ethBalance = getEthBalanceFrom(balances)
return (
<Row grow>
<Col sm={12} top="xs" md={5} margin="xl" overflow>
<List style={listStyle}>
<Balance balance={balance} />
<BalanceInfo balances={balances} />
<Owners
owners={safe.owners}
onAddOwner={this.onAddOwner}
@ -106,8 +120,8 @@ class GnoSafe extends React.PureComponent<SafeProps, State> {
/>
<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} />
<DailyLimit balance={ethBalance} dailyLimit={safe.get('dailyLimit')} onWithdraw={this.onWithdraw} onEditDailyLimit={this.onEditDailyLimit} />
<MultisigTx balance={ethBalance} onAddTx={this.onAddTx} onSeeTxs={this.onListTransactions} />
</List>
</Col>
<Col sm={12} center="xs" md={7} margin="xl" layout="column">

View File

@ -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 { type Safe } from '~/routes/safe/store/model/safe'
import { buildExecutedConfirmationFrom, storeTransaction } from '~/routes/safe/component/AddTransaction/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')

View File

@ -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')
})
})

View File

@ -1,13 +1,13 @@
// @flow
import fetchSafe from '~/routes/safe/store/actions/fetchSafe'
import fetchBalance from '~/routes/safe/store/actions/fetchBalance'
import { fetchBalances } from '~/routes/safe/store/actions/fetchBalances'
export type Actions = {
fetchSafe: typeof fetchSafe,
fetchBalance: typeof fetchBalance,
fetchBalances: typeof fetchBalances,
}
export default {
fetchSafe,
fetchBalance,
fetchBalances,
}

View File

@ -14,16 +14,27 @@ type Props = Actions & SelectorProps & {
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, fetchBalances, fetchSafe } = this.props
if (!safe) {
return
}
const safeAddress = safe.get('address')
fetchBalances(safeAddress)
fetchSafe(safe)
}, 1500)
}
componentDidUpdate(prevProps) {
if (prevProps.safe) {
return
}
if (this.props.safe) {
const safeAddress = this.props.safe.get('address')
this.props.fetchBalances(safeAddress)
}
}
componentWillUnmount() {
clearInterval(this.intervalId)
}
@ -32,13 +43,13 @@ class SafeView extends React.PureComponent<Props> {
render() {
const {
safe, provider, balance, granted, userAddress,
safe, provider, balances, granted, userAddress,
} = this.props
return (
<Page>
{ granted
? <Layout balance={balance} provider={provider} safe={safe} userAddress={userAddress} />
? <Layout balances={balances} provider={provider} safe={safe} userAddress={userAddress} />
: <NoRights />
}
</Page>

View File

@ -1,5 +1,5 @@
// @flow
import { List } from 'immutable'
import { List, Map } from 'immutable'
import { createSelector, createStructuredSelector, type Selector } from 'reselect'
import { balanceSelector, safeSelector, type RouterProps, type SafeSelectorProps } from '~/routes/safe/store/selectors'
import { providerNameSelector, userAccountSelector } from '~/wallets/store/selectors/index'
@ -7,11 +7,12 @@ 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 { type Balance } from '~/routes/safe/store/model/balance'
export type SelectorProps = {
safe: SafeSelectorProps,
provider: string,
balance: string,
balances: Map<string, Balance>,
userAddress: string,
}
@ -39,7 +40,7 @@ export const grantedSelector: Selector<GlobalState, RouterProps, boolean> = crea
export default createStructuredSelector({
safe: safeSelector,
provider: providerNameSelector,
balance: balanceSelector,
balances: balanceSelector,
granted: grantedSelector,
userAddress: userAccountSelector,
})

View File

@ -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

View File

@ -0,0 +1,21 @@
// @flow
import { Map } from 'immutable'
import { createAction } from 'redux-actions'
import { type Balance } from '~/routes/safe/store/model/balance'
export const ADD_BALANCES = 'ADD_BALANCES'
type BalanceProps = {
safeAddress: string,
balances: Map<string, Balance>,
}
const addBalances = createAction(
ADD_BALANCES,
(safeAddress: string, balances: Map<string, Balance>): BalanceProps => ({
safeAddress,
balances,
}),
)
export default addBalances

View File

@ -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))
}

View File

@ -0,0 +1,59 @@
// @flow
import { Map } from 'immutable'
import contract from 'truffle-contract'
import type { Dispatch as ReduxDispatch } from 'redux'
import StandardToken from '@gnosis.pm/util-contracts/build/contracts/StandardToken.json'
import { getBalanceInEtherOf, getWeb3 } from '~/wallets/getWeb3'
import { type GlobalState } from '~/store/index'
import { makeBalance, type Balance, type BalanceProps } from '~/routes/safe/store/model/balance'
import logo from '~/assets/icons/icon_etherTokens.svg'
import addBalances from './addBalances'
export const calculateBalanceOf = async (tokenAddress: string, address: string) => {
const web3 = getWeb3()
const erc20Token = await contract(StandardToken)
erc20Token.setProvider(web3.currentProvider)
return erc20Token.at(tokenAddress)
.then(instance => instance.balanceOf(address).then(funds => funds.toString()))
.catch(() => '0')
}
export const fetchBalances = (safeAddress: string) => async (dispatch: ReduxDispatch<GlobalState>) => {
const balance = await getBalanceInEtherOf(safeAddress)
const ethBalance = makeBalance({
address: '0',
name: 'Ether',
symbol: 'ETH',
decimals: 18,
logoUrl: logo,
funds: balance,
})
const header = new Headers({
'Access-Control-Allow-Origin': '*',
})
const sentData = {
mode: 'cors',
header,
}
const response = await fetch('https://gist.githubusercontent.com/rmeissner/98911fcf74b0ea9731e2dae2441c97a4/raw/', sentData)
if (!response.ok) {
throw new Error('Error querying safe balances')
}
const json = await response.json()
const balancesRecords = await Promise.all(json.map(async (item: BalanceProps) => {
const funds = await calculateBalanceOf(item.address, safeAddress)
return makeBalance({ ...item, funds })
}))
const balances: Map<string, Balance> = Map().withMutations((map) => {
balancesRecords.forEach(record => map.set(record.get('symbol'), record))
map.set('ETH', ethBalance)
})
return dispatch(addBalances(safeAddress, balances))
}

View File

@ -0,0 +1,23 @@
// @flow
import { Record } from 'immutable'
import type { RecordFactory, RecordOf } from 'immutable'
export type BalanceProps = {
address: string,
name: string,
symbol: string,
decimals: number,
logoUrl: string,
funds: string,
}
export const makeBalance: RecordFactory<BalanceProps> = Record({
address: '',
name: '',
symbol: '',
decimals: 0,
logoUrl: '',
funds: '0',
})
export type Balance = RecordOf<BalanceProps>

View File

@ -1,13 +1,20 @@
// @flow
import { Map } from 'immutable'
import { handleActions, type ActionType } from 'redux-actions'
import addBalance, { ADD_BALANCE } from '~/routes/safe/store/actions/addBalance'
import addBalances, { ADD_BALANCES } from '~/routes/safe/store/actions/addBalances'
import { type Balance } from '~/routes/safe/store/model/balance'
export const BALANCE_REDUCER_ID = 'balances'
export type State = Map<string, string>
export type State = Map<string, Map<string, Balance>>
export default handleActions({
[ADD_BALANCE]: (state: State, action: ActionType<typeof addBalance>): State =>
state.set(action.payload.safeAddress, action.payload.funds),
[ADD_BALANCES]: (state: State, action: ActionType<typeof addBalances>): State =>
state.update(action.payload.safeAddress, (prevSafe: Map<string, Balance>) => {
if (!prevSafe) {
return action.payload.balances
}
return prevSafe.equals(action.payload.balances) ? prevSafe : action.payload.balances
}),
}, Map())

View File

@ -10,6 +10,7 @@ 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'
import { type Balance } from '~/routes/safe/store/model/balance'
export type RouterProps = {
match: Match,
@ -81,15 +82,15 @@ export const safeSelector: Selector<GlobalState, RouterProps, SafeSelectorProps>
},
)
export const balanceSelector: Selector<GlobalState, RouterProps, string> = createSelector(
export const balanceSelector: Selector<GlobalState, RouterProps, Map<string, Balance>> = createSelector(
balancesSelector,
safeParamAddressSelector,
(balances: Map<string, string>, address: string) => {
(balances: Map<string, Map<string, Balance>>, address: string) => {
if (!address) {
return '0'
return Map()
}
return balances.get(address) || '0'
return balances.get(address) || Map()
},
)

View File

@ -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

View File

@ -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
*/

View File

@ -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()

View File

@ -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'))
})
})

View File

@ -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')
})
})

View File

@ -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'))
})
})

View File

@ -3,9 +3,10 @@ import * as React from 'react'
import TestUtils from 'react-dom/test-utils'
import { Provider } from 'react-redux'
import { ConnectedRouter } from 'react-router-redux'
import { type Match } from 'react-router-dom'
import Button from '~/components/layout/Button'
import { aNewStore, history } 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 { SAFELIST_ADDRESS } from '~/routes/routes'
import SafeView from '~/routes/safe/component/Safe'
@ -84,6 +85,7 @@ describe('React DOM TESTS > Withdraw funds from safe', () => {
const match: Match = buildMathPropsFrom(address)
const safe = safeSelector(store.getState(), { match })
if (!safe) throw new Error()
await executeWithdrawOn(safe, 0.01)
await executeWithdrawOn(safe, 0.01)

View File

@ -3,7 +3,7 @@ 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 { addEtherTo } from '~/test/utils/tokenMovements'
import SafeView from '~/routes/safe/component/Safe'
import TransactionsComponent from '~/routes/safe/component/Transactions'
import TransactionComponent from '~/routes/safe/component/Transactions/Transaction'

View File

@ -1,74 +0,0 @@
// @flow
import TestUtils from 'react-dom/test-utils'
import { store } from '~/store'
import {
FIELD_NAME,
FIELD_OWNERS,
FIELD_CONFIRMATIONS,
FIELD_DAILY_LIMIT,
getOwnerNameBy,
getOwnerAddressBy,
} from '~/routes/open/components/fields'
import { DEPLOYED_COMPONENT_ID } from '~/routes/open/components/FormConfirmation'
import { sleep } from '~/utils/timer'
import { getProviderInfo } from '~/wallets/getWeb3'
import { renderSafe } from '~/routes/safe/store/test/builder/deployedSafe.builder'
describe('React DOM TESTS > Create Safe form', () => {
let open
let provider
beforeEach(async () => {
provider = await getProviderInfo()
open = await renderSafe(store)
})
it('should create a 1 owner safe after rendering correctly the form', async () => {
const inputs = TestUtils.scryRenderedDOMComponentsWithTag(open, 'input')
const fieldName = inputs[0]
expect(fieldName.name).toEqual(FIELD_NAME)
const fieldOwners = inputs[1]
expect(fieldOwners.name).toEqual(FIELD_OWNERS)
const fieldConfirmations = inputs[2]
expect(fieldConfirmations.name).toEqual(FIELD_CONFIRMATIONS)
const dailyLimitConfirmations = inputs[3]
expect(dailyLimitConfirmations.name).toEqual(FIELD_DAILY_LIMIT)
TestUtils.Simulate.change(fieldOwners, { target: { value: '1' } })
const inputsExpanded = TestUtils.scryRenderedDOMComponentsWithTag(open, 'input')
const ownerName = inputsExpanded[2]
expect(ownerName.name).toEqual(getOwnerNameBy(0))
const ownerAddress = inputsExpanded[3]
expect(ownerAddress.name).toEqual(getOwnerAddressBy(0))
expect(ownerAddress.value).toEqual(provider.account)
// WHEN
TestUtils.Simulate.change(fieldName, { target: { value: 'Adolfo Safe' } })
TestUtils.Simulate.change(fieldConfirmations, { target: { value: '1' } })
TestUtils.Simulate.change(ownerName, { target: { value: 'Adolfo Eth Account' } })
TestUtils.Simulate.change(dailyLimitConfirmations, { target: { value: '10' } })
const form = TestUtils.findRenderedDOMComponentWithTag(open, 'form')
// One submit per step when creating a safe
TestUtils.Simulate.submit(form) // fill the form
TestUtils.Simulate.submit(form) // confirming data
TestUtils.Simulate.submit(form) // Executing transaction
// giving some time to the component for updating its state with safe
// before destroying its context
await sleep(9000)
// THEN
const deployed = TestUtils.findRenderedDOMComponentWithClass(open, DEPLOYED_COMPONENT_ID)
if (deployed) {
const transaction = JSON.parse(deployed.getElementsByTagName('pre')[0].innerHTML)
delete transaction.receipt.logsBloom
// eslint-disable-next-line
// console.log(transaction)
}
})
})

View File

@ -4,115 +4,33 @@ import { type Store } from 'redux'
import TestUtils from 'react-dom/test-utils'
import { Provider } from 'react-redux'
import { ConnectedRouter } from 'react-router-redux'
import { DEPLOYED_COMPONENT_ID } from '~/routes/open/components/FormConfirmation'
import Open from '~/routes/open/container/Open'
import SafeView from '~/routes/safe/component/Safe'
import { aNewStore, history, type GlobalState } from '~/store'
import { sleep } from '~/utils/timer'
import { getProviderInfo, getWeb3 } from '~/wallets/getWeb3'
import addProvider from '~/wallets/store/actions/addProvider'
import { makeProvider } from '~/wallets/store/model/provider'
import { getWeb3 } from '~/wallets/getWeb3'
import AppRoutes from '~/routes'
import { SAFELIST_ADDRESS } from '~/routes/routes'
import { promisify } from '~/utils/promisify'
import { addEtherTo } from '~/test/utils/etherMovements'
const fillOpenSafeForm = async (localStore: Store<GlobalState>) => {
const provider = await getProviderInfo()
const walletRecord = makeProvider(provider)
localStore.dispatch(addProvider(walletRecord))
return (
TestUtils.renderIntoDocument((
<Provider store={localStore}>
<ConnectedRouter history={history}>
<Open />
</ConnectedRouter>
</Provider>
))
)
}
const deploySafe = async (safe: React$Component<{}>, dailyLimit: string, threshold: number, numOwners: number) => {
expect(threshold).toBeLessThanOrEqual(numOwners)
const inputs = TestUtils.scryRenderedDOMComponentsWithTag(safe, 'input')
const fieldName = inputs[0]
const fieldOwners = inputs[1]
const fieldConfirmations = inputs[2]
const fieldDailyLimit = inputs[3]
const web3 = getWeb3()
const accounts = await promisify(cb => web3.eth.getAccounts(cb))
TestUtils.Simulate.change(fieldOwners, { target: { value: `${numOwners}` } })
await sleep(1500)
const inputsExpanded = TestUtils.scryRenderedDOMComponentsWithTag(safe, 'input')
expect(inputsExpanded.length).toBe((numOwners * 2) + 4) // 2 per owner + name, dailyLimit, confirmations, numOwners
for (let i = 0; i < numOwners; i += 1) {
const nameIndex = (i * 2) + 2
const addressIndex = (i * 2) + 3
const ownerName = inputsExpanded[nameIndex]
const account = inputsExpanded[addressIndex]
TestUtils.Simulate.change(ownerName, { target: { value: `Adolfo ${i + 1} Eth Account` } })
TestUtils.Simulate.change(account, { target: { value: accounts[i] } })
}
TestUtils.Simulate.change(fieldName, { target: { value: 'Adolfo Safe' } })
TestUtils.Simulate.change(fieldConfirmations, { target: { value: `${threshold}` } })
TestUtils.Simulate.change(fieldDailyLimit, { target: { value: dailyLimit } })
const form = TestUtils.findRenderedDOMComponentWithTag(safe, 'form')
TestUtils.Simulate.submit(form) // fill the form
TestUtils.Simulate.submit(form) // confirming data
TestUtils.Simulate.submit(form) // Executing transaction
// giving some time to the component for updating its state with safe
// before destroying its context
await sleep(15000)
// THEN
const deployed = TestUtils.findRenderedDOMComponentWithClass(safe, DEPLOYED_COMPONENT_ID)
if (!deployed) {
throw new Error()
}
const transactionHash = JSON.parse(deployed.getElementsByTagName('pre')[0].innerHTML)
delete transactionHash.receipt.logsBloom
return transactionHash
}
const aDeployedSafe = async (
specificStore: Store<GlobalState>,
dailyLimit?: number = 0.5,
threshold?: number = 1,
numOwners?: number = 1,
) => {
const safe: React$Component<{}> = await fillOpenSafeForm(specificStore)
const deployedSafe = await deploySafe(safe, `${dailyLimit}`, threshold, numOwners)
return deployedSafe.logs[1].args.proxy
}
import { addEtherTo } from '~/test/utils/tokenMovements'
import { aMinedSafe } from '~/test/builder/safe.redux.builder'
export type DomSafe = {
address: string,
safeButtons: Element[],
safe: React$Component<any, any>,
accounts: string[],
store: Store<GlobalState>,
}
export const renderSafeInDom = async (
owners: number,
threshold: number,
owners: number = 1,
threshold: number = 1,
dailyLimit: number = 0.5,
): Promise<DomSafe> => {
// create store
const store = aNewStore()
// deploy safe updating store
const address = await aDeployedSafe(store, dailyLimit, threshold, owners)
const address = await aMinedSafe(store, owners, threshold, dailyLimit)
// have available accounts
const accounts = await promisify(cb => getWeb3().eth.getAccounts(cb))
// navigate to SAFE route

View File

@ -4,14 +4,16 @@ import ListItemText from '~/components/List/ListItemText/index'
import { SEE_MULTISIG_BUTTON_TEXT } from '~/routes/safe/component/Safe/MultisigTx'
import fetchTransactions from '~/routes/safe/store/actions/fetchTransactions'
import { sleep } from '~/utils/timer'
import { type GlobalState } from '~/store/index'
export const EXPAND_OWNERS_INDEX = 0
export const ADD_OWNERS_INDEX = 1
export const EDIT_THRESHOLD_INDEX = 2
export const EDIT_INDEX = 3
export const WITHDRAW_INDEX = 4
export const MOVE_FUNDS_INDEX = 5
export const LIST_TXS_INDEX = 6
export const EXPAND_BALANCE_INDEX = 0
export const EXPAND_OWNERS_INDEX = 1
export const ADD_OWNERS_INDEX = 2
export const EDIT_THRESHOLD_INDEX = 3
export const EDIT_INDEX = 4
export const WITHDRAW_INDEX = 5
export const MOVE_FUNDS_INDEX = 6
export const LIST_TXS_INDEX = 7
export const listTxsClickingOn = async (seeTxsButton: Element) => {
expect(seeTxsButton.getElementsByTagName('span')[0].innerHTML).toEqual(SEE_MULTISIG_BUTTON_TEXT)
@ -79,7 +81,7 @@ export const checkPendingTx = async (
}
}
export const refreshTransactions = async (store) => {
export const refreshTransactions = async (store: Store<GlobalState>) => {
await store.dispatch(fetchTransactions())
await sleep(1500)
}

View File

@ -0,0 +1,112 @@
// @flow
import { makeSafe, type Safe } from '~/routes/safe/store/model/safe'
import { buildOwnersFrom, buildDailyLimitFrom } from '~/routes/safe/store/actions'
import { FIELD_NAME, FIELD_CONFIRMATIONS, FIELD_OWNERS, getOwnerNameBy, getOwnerAddressBy, FIELD_DAILY_LIMIT } from '~/routes/open/components/fields'
import { getWeb3, getProviderInfo } from '~/wallets/getWeb3'
import { promisify } from '~/utils/promisify'
import addSafe from '~/routes/safe/store/actions/addSafe'
import { createSafe, type OpenState } from '~/routes/open/container/Open'
import { type GlobalState } from '~/store/index'
import { makeProvider } from '~/wallets/store/model/provider'
import addProvider from '~/wallets/store/actions/addProvider'
class SafeBuilder {
safe: Safe
constructor() {
this.safe = makeSafe()
}
withAddress(address: string) {
this.safe = this.safe.set('address', address)
return this
}
withName(name: string) {
this.safe = this.safe.set('name', name)
return this
}
withConfirmations(confirmations: number) {
this.safe = this.safe.set('threshold', confirmations)
return this
}
withDailyLimit(limit: number, spentToday: number = 0) {
const dailyLimit = buildDailyLimitFrom(limit, spentToday)
this.safe = this.safe.set('dailyLimit', dailyLimit)
return this
}
withOwner(names: string[], adresses: string[]) {
const owners = buildOwnersFrom(names, adresses)
this.safe = this.safe.set('owners', owners)
return this
}
get() {
return this.safe
}
}
const aSafe = () => new SafeBuilder()
export class SafeFactory {
static oneOwnerSafe = (ownerAddress: string = '0x03db1a8b26d08df23337e9276a36b474510f0023') => aSafe()
.withAddress('0x03db1a8b26d08df23337e9276a36b474510f0025')
.withName('Adol ICO Safe')
.withConfirmations(1)
.withDailyLimit(10)
.withOwner(['Adol Metamask'], [ownerAddress])
.get()
static twoOwnersSafe = (firstOwner: string = '0x03db1a8b26d08df23337e9276a36b474510f0023', secondOwner: string = '0x03db1a8b26d08df23337e9276a36b474510f0024') => aSafe()
.withAddress('0x03db1a8b26d08df23337e9276a36b474510f0026')
.withName('Adol & Tobias Safe')
.withConfirmations(2)
.withOwner(
['Adol Metamask', 'Tobias Metamask'],
[firstOwner, secondOwner],
)
.withDailyLimit(10, 1.34)
.get()
static dailyLimitSafe = (dailyLimit: number, spentToday: number) => aSafe()
.withAddress('0x03db1a8b26d08df23337e9276a36b474510f0027')
.withName('Adol & Tobias Safe')
.withConfirmations(2)
.withOwner(
['Adol Metamask', 'Tobias Metamask'],
['0x03db1a8b26d08df23337e9276a36b474510f0023', '0x03db1a8b26d08df23337e9276a36b474510f0024'],
)
.withDailyLimit(dailyLimit, spentToday)
.get()
}
export const aMinedSafe = async (
store: Store<GlobalState>,
owners: number = 1,
threshold: number = 1,
dailyLimit: number = 0.5,
): Promise<string> => {
const provider = await getProviderInfo()
const walletRecord = makeProvider(provider)
store.dispatch(addProvider(walletRecord))
const accounts = await promisify(cb => getWeb3().eth.getAccounts(cb))
const form = {
[FIELD_NAME]: 'Safe Name',
[FIELD_CONFIRMATIONS]: `${threshold}`,
[FIELD_OWNERS]: `${owners}`,
[getOwnerNameBy(0)]: 'Adolfo 1 Eth Account',
[getOwnerAddressBy(0)]: accounts[0],
[FIELD_DAILY_LIMIT]: `${dailyLimit}`,
}
const addSafeFn: any = (...args) => store.dispatch(addSafe(...args))
const openSafeProps: OpenState = await createSafe(form, accounts[0], addSafeFn)
return openSafeProps.safeAddress
}
export default aSafe

View File

@ -0,0 +1,107 @@
// @flow
import * as React from 'react'
import { type Store } from 'redux'
import TestUtils from 'react-dom/test-utils'
import { Provider } from 'react-redux'
import { ConnectedRouter } from 'react-router-redux'
import { DEPLOYED_COMPONENT_ID } from '~/routes/open/components/FormConfirmation'
import Open from '~/routes/open/container/Open'
import { aNewStore, history, type GlobalState } from '~/store'
import { sleep } from '~/utils/timer'
import { getProviderInfo, getWeb3 } from '~/wallets/getWeb3'
import addProvider from '~/wallets/store/actions/addProvider'
import { makeProvider } from '~/wallets/store/model/provider'
import { promisify } from '~/utils/promisify'
const fillOpenSafeForm = async (localStore: Store<GlobalState>) => {
const provider = await getProviderInfo()
const walletRecord = makeProvider(provider)
localStore.dispatch(addProvider(walletRecord))
return (
TestUtils.renderIntoDocument((
<Provider store={localStore}>
<ConnectedRouter history={history}>
<Open />
</ConnectedRouter>
</Provider>
))
)
}
const deploySafe = async (safe: React$Component<{}>, dailyLimit: string, threshold: number, numOwners: number) => {
expect(threshold).toBeLessThanOrEqual(numOwners)
const inputs = TestUtils.scryRenderedDOMComponentsWithTag(safe, 'input')
const fieldName = inputs[0]
const fieldOwners = inputs[1]
const fieldConfirmations = inputs[2]
const fieldDailyLimit = inputs[3]
const web3 = getWeb3()
const accounts = await promisify(cb => web3.eth.getAccounts(cb))
TestUtils.Simulate.change(fieldOwners, { target: { value: `${numOwners}` } })
await sleep(1500)
const inputsExpanded = TestUtils.scryRenderedDOMComponentsWithTag(safe, 'input')
expect(inputsExpanded.length).toBe((numOwners * 2) + 4) // 2 per owner + name, dailyLimit, confirmations, numOwners
for (let i = 0; i < numOwners; i += 1) {
const nameIndex = (i * 2) + 2
const addressIndex = (i * 2) + 3
const ownerName = inputsExpanded[nameIndex]
const account = inputsExpanded[addressIndex]
TestUtils.Simulate.change(ownerName, { target: { value: `Adolfo ${i + 1} Eth Account` } })
TestUtils.Simulate.change(account, { target: { value: accounts[i] } })
}
TestUtils.Simulate.change(fieldName, { target: { value: 'Adolfo Safe' } })
TestUtils.Simulate.change(fieldConfirmations, { target: { value: `${threshold}` } })
TestUtils.Simulate.change(fieldDailyLimit, { target: { value: dailyLimit } })
const form = TestUtils.findRenderedDOMComponentWithTag(safe, 'form')
TestUtils.Simulate.submit(form) // fill the form
TestUtils.Simulate.submit(form) // confirming data
TestUtils.Simulate.submit(form) // Executing transaction
// giving some time to the component for updating its state with safe
// before destroying its context
await sleep(8000)
// THEN
const deployed = TestUtils.findRenderedDOMComponentWithClass(safe, DEPLOYED_COMPONENT_ID)
if (!deployed) {
throw new Error()
}
const transactionHash = JSON.parse(deployed.getElementsByTagName('pre')[0].innerHTML)
delete transactionHash.receipt.logsBloom
return transactionHash
}
const aDeployedSafe = async (
specificStore: Store<GlobalState>,
dailyLimit?: number = 0.5,
threshold?: number = 1,
numOwners?: number = 1,
) => {
const safe: React$Component<{}> = await fillOpenSafeForm(specificStore)
const deployedSafe = await deploySafe(safe, `${dailyLimit}`, threshold, numOwners)
return deployedSafe.logs[1].args.proxy
}
describe('DOM > Feature > CREATE a safe', () => {
it('fills correctly the safe form with 4 owners, 4 threshold and 5 ETH as daily limit', async () => {
const owners = 4
const threshold = 4
const dailyLimit = 5
const store = aNewStore()
const address = await aDeployedSafe(store, dailyLimit, threshold, owners)
expect(address).not.toBe(null)
expect(address).not.toBe(undefined)
})
})

View File

@ -9,7 +9,7 @@ import { sendRemoveOwnerForm, checkMinedRemoveOwnerTx, checkPendingRemoveOwnerTx
import { checkMinedThresholdTx, sendChangeThresholdForm, checkThresholdOf } from '~/test/utils/transactions/threshold.helper'
import { sendWithdrawForm, checkMinedWithdrawTx } from '~/test/utils/transactions/withdraw.helper'
import { processTransaction } from '~/routes/safe/component/Transactions/processTransactions'
import { checkBalanceOf } from '~/test/utils/etherMovements'
import { checkBalanceOf } from '~/test/utils/tokenMovements'
describe('DOM > Feature > SAFE MULTISIG TX 1 Owner 1 Threshold', () => {
let domSafe: DomSafe

View File

@ -0,0 +1,87 @@
// @flow
import { Map } from 'immutable'
import { BALANCE_REDUCER_ID } from '~/routes/safe/store/reducer/balances'
import * as fetchBalancesAction from '~/routes/safe/store/actions/fetchBalances'
import { aNewStore } from '~/store'
import { aMinedSafe } from '~/test/builder/safe.redux.builder'
import { type Balance, makeBalance } from '~/routes/safe/store/model/balance'
import addBalances from '~/routes/safe/store/actions/addBalances'
import { addEtherTo, addTknTo } from '~/test/utils/tokenMovements'
describe('Safe Actions[fetchBalance]', () => {
let store
let address: string
beforeEach(async () => {
store = aNewStore()
address = await aMinedSafe(store)
})
it('reducer should return 0 to just deployed safe', async () => {
// GIVEN
const tokenList = ['WE', '<3', 'GNO', 'OMG', 'RDN']
// WHEN
await store.dispatch(fetchBalancesAction.fetchBalances(address))
// THEN
const balances: Map<string, Map<string, Balance>> | typeof undefined = store.getState()[BALANCE_REDUCER_ID]
if (!balances) throw new Error()
const safeBalances: Map<string, Balance> | typeof undefined = balances.get(address)
if (!safeBalances) throw new Error()
expect(safeBalances.size).toBe(6)
tokenList.forEach((token: string) => {
const record = safeBalances.get(token)
if (!record) throw new Error()
expect(record.get('funds')).toBe('0')
})
})
it('reducer should return 0.03456 ETH as funds to safe with 0.03456 ETH', async () => {
// WHEN
await addEtherTo(address, '0.03456')
await store.dispatch(fetchBalancesAction.fetchBalances(address))
// THEN
const balances: Map<string, Map<string, Balance>> | typeof undefined = store.getState()[BALANCE_REDUCER_ID]
if (!balances) throw new Error()
const safeBalances: Map<string, Balance> | typeof undefined = balances.get(address)
if (!safeBalances) throw new Error()
expect(safeBalances.size).toBe(6)
const ethBalance = safeBalances.get('ETH')
if (!ethBalance) throw new Error()
expect(ethBalance.get('funds')).toBe('0.03456')
})
it('reducer should return 100 TKN when safe has 100 TKN', async () => {
// GIVEN
const numTokens = 100
const tokenAddress = await addTknTo(address, numTokens)
// WHEN
const fetchBalancesMock = jest.spyOn(fetchBalancesAction, 'fetchBalances')
const funds = await fetchBalancesAction.calculateBalanceOf(tokenAddress, address)
const balances: Map<string, Balance> = Map().set('TKN', makeBalance({
address: tokenAddress,
name: 'Token',
symbol: 'TKN',
decimals: 18,
logoUrl: 'https://github.com/TrustWallet/tokens/blob/master/images/0x6810e776880c02933d47db1b9fc05908e5386b96.png?raw=true',
funds,
}))
fetchBalancesMock.mockImplementation(() => store.dispatch(addBalances(address, balances)))
await store.dispatch(fetchBalancesAction.fetchBalances(address))
fetchBalancesMock.mockRestore()
// THEN
const safeBalances = store.getState()[BALANCE_REDUCER_ID].get(address)
expect(safeBalances.size).toBe(1)
const tknBalance = safeBalances.get('TKN')
expect(tknBalance.get('funds')).toBe(String(numTokens))
})
})

View File

@ -3,7 +3,7 @@ import { getWeb3 } from '~/wallets/getWeb3'
import abi from 'ethereumjs-abi'
import { promisify } from '~/utils/promisify'
export const getErrorMessage = async (to, value, data, from) => {
export const getErrorMessage = async (to: string, value: number, data: string, from: string) => {
const web3 = getWeb3()
const returnData = await promisify(cb => web3.eth.call({
to, from, value, data,

View File

@ -1,7 +1,10 @@
// @flow
import contract from 'truffle-contract'
import { getProviderInfo, getBalanceInEtherOf, getWeb3 } from '~/wallets/getWeb3'
import { promisify } from '~/utils/promisify'
import withdraw, { DESTINATION_PARAM, VALUE_PARAM } from '~/routes/safe/component/Withdraw/withdraw'
import { type Safe } from '~/routes/safe/store/model/safe'
import Token from '#/test/Token.json'
export const addEtherTo = async (address: string, eth: string) => {
const web3 = getWeb3()
@ -10,7 +13,7 @@ export const addEtherTo = async (address: string, eth: string) => {
return promisify(cb => web3.eth.sendTransaction(txData, cb))
}
export const executeWithdrawOn = async (safeAddress: string, value: number) => {
export const executeWithdrawOn = async (safe: Safe, value: number) => {
const providerInfo = await getProviderInfo()
const userAddress = providerInfo.account
@ -19,10 +22,21 @@ export const executeWithdrawOn = async (safeAddress: string, value: number) => {
[VALUE_PARAM]: `${value}`,
}
return withdraw(values, safeAddress, userAddress)
return withdraw(values, safe, userAddress)
}
export const checkBalanceOf = async (addressToTest: string, value: string) => {
const safeBalance = await getBalanceInEtherOf(addressToTest)
expect(safeBalance).toBe(value)
}
export const addTknTo = async (safe: string, value: number) => {
const web3 = getWeb3()
const token = contract(Token)
token.setProvider(web3.currentProvider)
const accounts = await promisify(cb => getWeb3().eth.getAccounts(cb))
const myToken = await token.new({ from: accounts[0], gas: '5000000' })
await myToken.transfer(safe, value, { from: accounts[0], gas: '5000000' })
return myToken.address
}

View File

@ -3,7 +3,7 @@ import TestUtils from 'react-dom/test-utils'
import { sleep } from '~/utils/timer'
import { checkMinedTx, checkPendingTx } from '~/test/builder/safe.dom.utils'
export const sendMoveFundsForm = (
export const sendMoveFundsForm = async (
SafeDom: React$Component<any, any>,
multisigButton: React$Component<any, any>,
txName: string,
@ -13,7 +13,7 @@ export const sendMoveFundsForm = (
// load add multisig form component
TestUtils.Simulate.click(multisigButton)
// give time to re-render it
sleep(600)
await sleep(600)
// fill the form
const inputs = TestUtils.scryRenderedDOMComponentsWithTag(SafeDom, 'input')

View File

@ -1,7 +1,7 @@
// @flow
import TestUtils from 'react-dom/test-utils'
import { sleep } from '~/utils/timer'
import { checkBalanceOf } from '~/test/utils/etherMovements'
import { checkBalanceOf } from '~/test/utils/tokenMovements'
import { checkMinedTx } from '~/test/builder/safe.dom.utils'
export const sendWithdrawForm = (
@ -36,7 +36,7 @@ export const checkMinedWithdrawTx = async (
Transaction: React$Component<any, any>,
name: string,
address: string,
funds: number,
funds: string,
) => {
await checkBalanceOf(address, funds)

View File

@ -931,6 +931,10 @@
lodash "^4.17.5"
to-fast-properties "^2.0.0"
"@gnosis.pm/util-contracts@^0.2.14":
version "0.2.14"
resolved "https://registry.yarnpkg.com/@gnosis.pm/util-contracts/-/util-contracts-0.2.14.tgz#587cd6268a7d08dbc0d08b1c7bd375e19549d833"
"@material-ui/core@^1.2.1":
version "1.2.2"
resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-1.2.2.tgz#b074bdaa679d68af235b4d3f108f828ddcf6c1bc"
@ -2921,14 +2925,14 @@ bignumber.js@^7.2.1:
version "7.2.1"
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f"
"bignumber.js@git+https://github.com/debris/bignumber.js#master":
version "2.0.7"
resolved "git+https://github.com/debris/bignumber.js#c7a38de919ed75e6fb6ba38051986e294b328df9"
"bignumber.js@git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2":
version "2.0.7"
resolved "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2"
"bignumber.js@git+https://github.com/debris/bignumber.js.git#master":
version "2.0.7"
resolved "git+https://github.com/debris/bignumber.js.git#c7a38de919ed75e6fb6ba38051986e294b328df9"
binary-extensions@^1.0.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205"