Forkchoice Ethereum Mainnet

Address Contract Partially Verified

Address 0x3650b99D107d581eF8ff004365A4Ada8DA6bf62F
Balance 0 ETH
Nonce 1
Code Size 11626 bytes
Indexed Transactions 0 (1 on-chain, 1.2% indexed)
External Etherscan · Sourcify

Contract Bytecode

11626 bytes
0x608060405234801561001057600080fd5b50600436106101f05760003560e01c80638beb60b61161010f578063b74d784e116100a2578063e1a9888e11610071578063e1a9888e14610fc3578063e30c397814611005578063f2fde38b1461104f578063fc0c546a14611093576101f0565b8063b74d784e14610dc6578063c3ac610d14610de4578063cb9b51c814610e71578063db518db214610f75576101f0565b8063a2d3cf4b116100de578063a2d3cf4b14610a3e578063adc4c87414610b5b578063b3ab15fb14610c60578063b619d3bd14610ca4576101f0565b80638beb60b61461087c5780638da5cb5b146108aa578063a0b9d101146108f4578063a0be06f914610a20576101f0565b8063543fd313116101875780636dd4a7c9116101565780636dd4a7c9146106ee5780636ef61092146107c457806371b150131461081c57806373d8903b1461083a576101f0565b8063543fd3131461054257806354fd4d501461059a578063570ca735146105b85780636c7a9d2414610602576101f0565b80634942e4cf116101c35780634942e4cf1461040a5780634b319713146104975780634beb62b7146104b55780634e71e0c814610538576101f0565b8063205c2878146101f5578063270231cc146102435780632e1a7d4d146102f057806332ef2b241461031e575b600080fd5b6102416004803603604081101561020b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506110dd565b005b6102ee6004803603608081101561025957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190929190803590602001906401000000008111156102aa57600080fd5b8201836020820111156102bc57600080fd5b803590602001918460208302840111640100000000831117156102de57600080fd5b90919293919293905050506110ec565b005b61031c6004803603602081101561030657600080fd5b81019080803590602001909291905050506111a1565b005b6104086004803603608081101561033457600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561038557600080fd5b82018360208201111561039757600080fd5b803590602001918460208302840111640100000000831117156103b957600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505091929192905050506111af565b005b6104956004803603606081101561042057600080fd5b8101908080359060200190929190803590602001909291908035906020019064010000000081111561045157600080fd5b82018360208201111561046357600080fd5b8035906020019184602083028401116401000000008311171561048557600080fd5b909192939192939050505061123e565b005b61049f611291565b6040518082815260200191505060405180910390f35b6104bd611297565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104fd5780820151818401526020810190506104e2565b50505050905090810190601f16801561052a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610540611335565b005b6105846004803603602081101561055857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061153d565b6040518082815260200191505060405180910390f35b6105a2611555565b6040518082815260200191505060405180910390f35b6105c061155b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6106ec6004803603608081101561061857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001909291908035906020019064010000000081111561066957600080fd5b82018360208201111561067b57600080fd5b8035906020019184602083028401116401000000008311171561069d57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290505050611581565b005b6107ae6004803603604081101561070457600080fd5b81019080803590602001909291908035906020019064010000000081111561072b57600080fd5b82018360208201111561073d57600080fd5b8035906020019184602083028401116401000000008311171561075f57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505091929192905050506115f4565b6040518082815260200191505060405180910390f35b610806600480360360208110156107da57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506116ab565b6040518082815260200191505060405180910390f35b6108246116c3565b6040518082815260200191505060405180910390f35b6108666004803603602081101561085057600080fd5b81019080803590602001909291905050506116c9565b6040518082815260200191505060405180910390f35b6108a86004803603602081101561089257600080fd5b81019080803590602001909291905050506116e1565b005b6108b2611865565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610a1e600480360360e081101561090a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561097157600080fd5b82018360208201111561098357600080fd5b803590602001918460018302840111640100000000831117156109a557600080fd5b90919293919293908035906020019092919080359060200190929190803590602001906401000000008111156109da57600080fd5b8201836020820111156109ec57600080fd5b80359060200191846020830284011164010000000083111715610a0e57600080fd5b909192939192939050505061188b565b005b610a286119b0565b6040518082815260200191505060405180910390f35b610b4160048036036080811015610a5457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190640100000000811115610abb57600080fd5b820183602082011115610acd57600080fd5b80359060200191846001830284011164010000000083111715610aef57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506119b6565b604051808215151515815260200191505060405180910390f35b610c5e60048036036080811015610b7157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190640100000000811115610bd857600080fd5b820183602082011115610bea57600080fd5b80359060200191846001830284011164010000000083111715610c0c57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050611c97565b005b610ca260048036036020811015610c7657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611d26565b005b610dc4600480360360c0811015610cba57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610d1757600080fd5b820183602082011115610d2957600080fd5b80359060200191846001830284011164010000000083111715610d4b57600080fd5b9091929391929390803590602001909291908035906020019092919080359060200190640100000000811115610d8057600080fd5b820183602082011115610d9257600080fd5b80359060200191846020830284011164010000000083111715610db457600080fd5b9091929391929390505050611e70565b005b610dce611fec565b6040518082815260200191505060405180910390f35b610e6f60048036036060811015610dfa57600080fd5b81019080803590602001909291908035906020019092919080359060200190640100000000811115610e2b57600080fd5b820183602082011115610e3d57600080fd5b80359060200191846001830284011164010000000083111715610e5f57600080fd5b9091929391929390505050611ff2565b005b610f5b60048036036080811015610e8757600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190640100000000811115610ed857600080fd5b820183602082011115610eea57600080fd5b80359060200191846020830284011164010000000083111715610f0c57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050919291929050505061219a565b604051808215151515815260200191505060405180910390f35b610fc160048036036040811015610f8b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506122ad565b005b610fef60048036036020811015610fd957600080fd5b81019080803590602001909291905050506122bc565b6040518082815260200191505060405180910390f35b61100d6122d4565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6110916004803603602081101561106557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506122fa565b005b61109b612401565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6110e8823383612427565b5050565b611139843385858580806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050506111af565b600061118d600b60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548561276390919063ffffffff16565b905061119986826110dd565b505050505050565b6111ac333383612427565b50565b6111bb8484848461219a565b61122d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600b8152602001807f6572726f725f70726f6f6600000000000000000000000000000000000000000081525060200191505060405180910390fd5b6112388484846127ad565b50505050565b61128b338585858580806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050611581565b50505050565b60085481565b600c8054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561132d5780601f106113025761010080835404028352916020019161132d565b820191906000526020600020905b81548152906001019060200180831161131057829003601f168201915b505050505081565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146113f8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f6572726f725f6f6e6c7950656e64696e674f776e65720000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600a6020528060005260406000206000915090505481565b600d5481565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b61158d838584846111af565b60006115e1600b60008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548461276390919063ffffffff16565b90506115ed85826122ad565b5050505050565b600082905060008090505b82518160ff1610156116a4576000838260ff168151811061161c57fe5b60200260200101519050808310156116645782816040516020018083815260200182815260200192505050604051602081830303815290604052805190602001209250611696565b808360405160200180838152602001828152602001925050506040516020818303038152906040528051906020012092505b5080806001019150506115ff565b5092915050565b600b6020528060005260406000206000915090505481565b60035481565b60046020528060005260406000206000915090505481565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146117a4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f6572726f725f6f6e6c794f776e6572000000000000000000000000000000000081525060200191505060405180910390fd5b670de0b6b3a7640000811115611822576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f6572726f725f61646d696e46656500000000000000000000000000000000000081525060200191505060405180910390fd5b806006819055507f11a80b766155f9b8f16a7da44d66269fd694cb1c247f4814244586f68dd534876006546040518082815260200191505060405180910390a150565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6118db89898989898080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506119b6565b61194d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f6572726f725f6261645369676e6174757265000000000000000000000000000081525060200191505060405180910390fd5b61199a848985858580806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050506111af565b6119a5898989612427565b505050505050505050565b60065481565b60006041825114611a2f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f6572726f725f6261645369676e61747572654c656e677468000000000000000081525060200191505060405180910390fd5b60008060006020850151925060408501519150606085015160001a9050601b8160ff161015611a5f57601b810190505b601b8160ff161480611a745750601c8160ff16145b611ae6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f6572726f725f6261645369676e617475726556657273696f6e0000000000000081525060200191505060405180910390fd5b6000888730600b60008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205460405160200180807f19457468657265756d205369676e6564204d6573736167653a0a313034000000815250601d018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b81526014018481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401828152602001945050505050604051602081830303815290604052805190602001209050600060018284878760405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611c4d573d6000803e3d6000fd5b5050506020604051035190508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161495505050505050949350505050565b611ca3848484846119b6565b611d15576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f6572726f725f6261645369676e6174757265000000000000000000000000000081525060200191505060405180910390fd5b611d20848484612427565b50505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611de9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f6572726f725f6f6e6c794f776e6572000000000000000000000000000000000081525060200191505060405180910390fd5b80600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167f4721129e0e676ed6a92909bb24e853ccdd63ad72280cc2e974e38e480e0e6e5460405160405180910390a250565b611ec18888600089898080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506119b6565b611f33576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f6572726f725f6261645369676e6174757265000000000000000000000000000081525060200191505060405180910390fd5b611f80848885858580806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050506111af565b6000611fd4600b60008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548561276390919063ffffffff16565b9050611fe1898983612427565b505050505050505050565b60095481565b6000801b600080868152602001908152602001600020541461207c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f6572726f725f6f7665727772697465000000000000000000000000000000000081525060200191505060405180910390fd5b606082828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505090506120d0858583612b0d565b83600080878152602001908152602001600020819055507f444dcb3cce8fbb3e1dabd2ff958f17fb1e673b759824d631d9cda0690d031eb98585836040518084815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561215757808201518184015260208101905061213c565b50505050905090810190601f1680156121845780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a15050505050565b600080848487604051602001808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401838152602001828152602001935050505060405160208183030381529060405280519060200120905060008060008881526020019081526020016000205490506000801b811415612295576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f6572726f725f626c6f636b4e6f74466f756e640000000000000000000000000081525060200191505060405180910390fd5b61229f82856115f4565b811492505050949350505050565b6122b8828383612427565b5050565b60006020528060005260406000206000915090505481565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146123bd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f6572726f725f6f6e6c794f776e6572000000000000000000000000000000000081525060200191505060405180910390fd5b80600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000811161249d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f6572726f725f7a65726f5769746864726177000000000000000000000000000081525060200191505060405180910390fd5b60006124f182600b60008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054612bed90919063ffffffff16565b9050600a60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548111156125a8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f6572726f725f6f7665726472616674000000000000000000000000000000000081525060200191505060405180910390fd5b80600b60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061260182600854612bed90919063ffffffff16565b600881905550600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb85846040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b1580156126b057600080fd5b505af11580156126c4573d6000803e3d6000fd5b505050506040513d60208110156126da57600080fd5b810190808051906020019092919050505061275d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f6572726f725f7472616e7366657200000000000000000000000000000000000081525060200191505060405180910390fd5b50505050565b60006127a583836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612c75565b905092915050565b6000600460008581526020019081526020016000205490506003548101421161283e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600c8152602001807f6572726f725f66726f7a656e000000000000000000000000000000000000000081525060200191505060405180910390fd5b81600a60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054106128f2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f6572726f725f6f6c644561726e696e677300000000000000000000000000000081525060200191505060405180910390fd5b612958600a60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461294a84600954612bed90919063ffffffff16565b61276390919063ffffffff16565b600981905550600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156129fd57600080fd5b505afa158015612a11573d6000803e3d6000fd5b505050506040513d6020811015612a2757600080fd5b8101908080519060200190929190505050612a4f60085460095461276390919063ffffffff16565b1115612ac3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f6572726f725f6d697373696e6742616c616e636500000000000000000000000081525060200191505060405180910390fd5b81600a60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050505050565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612bd0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f6572726f725f6e6f745065726d6974746564000000000000000000000000000081525060200191505060405180910390fd5b426004600085815260200190815260200160002081905550505050565b600080828401905083811015612c6b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b6000838311158290612d22576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015612ce7578082015181840152602081019050612ccc565b50505050905090810190601f168015612d145780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838503905080915050939250505056fea265627a7a723158206640614c1e318d8144727db96b3e60f3980eab48f5ee5fb614332bd1b358b38464736f6c63430005100032

Verified Source Code Partial Match

Compiler: v0.5.16+commit.9c3226ce EVM: istanbul Optimization: No
DataunionVault.sol 709 lines
pragma solidity ^0.5.16;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     *
     * _Available since v2.4.0._
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}


/**
 * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
 * the optional functions; to access them see {ERC20Detailed}.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}


/**
 * Abstract contract, requires implementation to specify who can commit blocks and what
 *   happens when a successful proof is presented
 * Verifies Merkle-tree inclusion proofs that show that certain address has
 *   certain earnings balance, according to hash published ("signed") by a
 *   sidechain operator or similar authority
 *
 * ABOUT Merkle-tree inclusion proof: Merkle-tree inclusion proof is an algorithm to prove memebership
 * in a set using minimal [ie log(N)] inputs. The hashes of the items are arranged by hash value in a binary Merkle tree where
 * each node contains a hash of the hashes of nodes below. The root node (ie "root hash") contains hash information
 * about the entire set, and that is the data that BalanceVerifier posts to the blockchain. To prove membership, you walk up the
 * tree from the node in question, and use the supplied hashes (the "proof") to fill in the hashes from the adjacent nodes. The proof
 * succeeds iff you end up with the known root hash when you get to the top of the tree.
 * See https://medium.com/crypto-0-nite/merkle-proofs-explained-6dd429623dc5
 *
 * Merkle-tree inclusion proof is a related concept to the blockchain Merkle tree, but a somewhat different application.
 * BalanceVerifier posts the root hash of the current ledger only, and this does not depend on the hash of previous ledgers.
 * This is different from the blockchain, where each block contains the hash of the previous block.
 *
 * TODO: see if it could be turned into a library, so many contracts could use it
 */
contract BalanceVerifier {
    event NewCommit(uint blockNumber, bytes32 rootHash, string ipfsHash);

    /**
     * Root hashes of merkle-trees constructed from its balances
     * @param uint root-chain block number after which the balances were committed
     * @return bytes32 root of the balances merkle-tree at that time
     */
    mapping (uint => bytes32) public committedHash;

    /**
     * Handler for proof of off-chain balances
     * It is up to the implementing contract to actually distribute out the balances
     * @param blockNumber the block whose hash was used for verification
     * @param account whose balances were successfully verified
     * @param balance the off-chain account balance
     */
    function onVerifySuccess(uint blockNumber, address account, uint balance) internal;

    /**
     * Implementing contract should should do access controls for committing
     */
    function onCommit(uint blockNumber, bytes32 rootHash, string memory ipfsHash) internal;

    /**
     * Monoplasma operator submits commitments to root-chain.
     * For convenience, also publish the ipfsHash of the balance book JSON object
     * @param blockNumber the root-chain block after which the balances were recorded
     * @param rootHash root of the balances merkle-tree
     * @param ipfsHash where the whole balances object can be retrieved in JSON format
     */
    function commit(uint blockNumber, bytes32 rootHash, string calldata ipfsHash) external {
        require(committedHash[blockNumber] == 0, "error_overwrite");
        string memory _hash = ipfsHash;
        onCommit(blockNumber, rootHash, _hash); // Access control delegated to implementing class
        committedHash[blockNumber] = rootHash;
        emit NewCommit(blockNumber, rootHash, _hash);
    }

    /**
     * Proving can be used to record the sidechain balances permanently into root chain
     * @param blockNumber the block after which the balances were recorded
     * @param account whose balances will be verified
     * @param balance off-chain account balance
     * @param proof list of hashes to prove the totalEarnings
     */
    function prove(uint blockNumber, address account, uint balance, bytes32[] memory proof) public {
        require(proofIsCorrect(blockNumber, account, balance, proof), "error_proof");
        onVerifySuccess(blockNumber, account, balance);
    }

    /**
     * Check the merkle proof of balance in the given commit (after blockNumber in root-chain) for given account
     * @param blockNumber the block after which the balances were recorded
     * @param account whose balances will be verified
     * @param balance off-chain account balance
     * @param proof list of hashes to prove the totalEarnings
     */
    function proofIsCorrect(uint blockNumber, address account, uint balance, bytes32[] memory proof) public view returns(bool) {
        bytes32 leafHash = keccak256(abi.encodePacked(account, balance, blockNumber));
        bytes32 rootHash = committedHash[blockNumber];
        require(rootHash != 0x0, "error_blockNotFound");
        return rootHash == calculateRootHash(leafHash, proof);
    }

    /**
     * Calculate root hash of a Merkle tree, given
     * @param leafHash of the member whose balances are being be verified
     * @param others list of hashes of "other" branches
     */
    function calculateRootHash(bytes32 leafHash, bytes32[] memory others) public pure returns (bytes32 root) {
        root = leafHash;
        for (uint8 i = 0; i < others.length; i++) {
            bytes32 other = others[i];
            if (root < other) {
                // TODO: consider hashing in i to defend from https://en.wikipedia.org/wiki/Merkle_tree#Second_preimage_attack
                root = keccak256(abi.encodePacked(root, other));
            } else {
                root = keccak256(abi.encodePacked(other, root));
            }
        }
    }
}


/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {
    address public owner;
    address public pendingOwner;

    event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
    );

    /**
     * @dev The Ownable constructor sets the original `owner` of the contract to the sender
     * account.
     */
    constructor() public {
        owner = msg.sender;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(msg.sender == owner, "error_onlyOwner");
        _;
    }

    /**
     * @dev Allows the current owner to set the pendingOwner address.
     * @param newOwner The address to transfer ownership to.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        pendingOwner = newOwner;
    }

    /**
     * @dev Allows the pendingOwner address to finalize the transfer.
     */
    function claimOwnership() public {
        require(msg.sender == pendingOwner, "error_onlyPendingOwner");
        emit OwnershipTransferred(owner, pendingOwner);
        owner = pendingOwner;
        pendingOwner = address(0);
    }
}




/**
 * Monoplasma that is managed by an owner, who also appoints a trusted (but verifiable) operator.
 * Owner should be able to add and remove recipients through an off-chain mechanism not specified here.
 */
contract Monoplasma is BalanceVerifier, Ownable {
    using SafeMath for uint256;

    event OperatorChanged(address indexed newOperator);
    event AdminFeeChanged(uint adminFee);

    /**
     * Freeze period during which all participants should be able to
     *   acquire the whole balance book from IPFS (or HTTP server, or elsewhere)
     *   and validate that the published rootHash is correct.
     * In case of incorrect rootHash, all members should issue withdrawals from the
     *   latest block they have validated (that is older than blockFreezeSeconds).
     * So: too short freeze period `+` bad availability `=>` ether (needlessly) spent withdrawing earnings.
     *     Long freeze period `==` lag between purchase and withdrawal `=>` bad UX.
     * Blocks older than blockFreezeSeconds can be used to withdraw funds.
     */
    uint public blockFreezeSeconds;

    /**
     * Block number => timestamp
     * Publish time of a block, where the block freeze period starts from.
     * Note that block number points to the block after which the root hash is calculated,
     *   not the block where NewCommit was emitted (event must come later)
     */
    mapping (uint => uint) public blockTimestamp;

    /// operator is the address who is allowed to commit the earnings
    address public operator;

    /// fee fraction = adminFee/10^18
    uint public adminFee;

    IERC20 public token;

    /// track lifetime total of tokens withdrawn from contract
    uint public totalWithdrawn;

    /**
     * Track lifetime total of earnings proven, as extra protection from malicious operator.
     * The difference of what CAN be withdrawn and what HAS been withdrawn must be covered with tokens in contract,
     *   in other words: `totalProven - totalWithdrawn <= token.balanceOf(this)`.
     * This is to prevent a "bank run" situation where more earnings have been proven in the contract than there are tokens to cover them.
     * Of course this only moves the "bank run" outside the contract, to a race to prove earnings,
     *   but at least the contract should never go into a state where it couldn't cover what's been proven.
     */
    uint public totalProven;

    /// earnings for which proof has been submitted
    mapping (address => uint) public earnings;

    /// earnings that have been sent out already
    mapping (address => uint) public withdrawn;

    constructor(address tokenAddress, uint blockFreezePeriodSeconds, uint initialAdminFee) public {
        blockFreezeSeconds = blockFreezePeriodSeconds;
        token = IERC20(tokenAddress);
        operator = msg.sender;
        setAdminFee(initialAdminFee);
    }

    /**
     * Admin can appoint the operator.
     * @param newOperator that is allowed to commit the off-chain balances
     */
    function setOperator(address newOperator) public onlyOwner {
        operator = newOperator;
        emit OperatorChanged(newOperator);
    }

    /**
     * Admin fee as a fraction of revenue.
     * Smart contract doesn't use it, it's here just for storing purposes.
     * @param newAdminFee fixed-point decimal in the same way as ether: 50% === 0.5 ether === "500000000000000000"
     */
    function setAdminFee(uint newAdminFee) public onlyOwner {
        require(newAdminFee <= 1 ether, "error_adminFee");
        adminFee = newAdminFee;
        emit AdminFeeChanged(adminFee);
    }

    /**
     * Operator commits the off-chain balances.
     * This starts the freeze period (measured from block.timestamp).
     * See README under "Threat model" for discussion on safety of using "now".
     * @param blockNumber after which balances were submitted
     */
    function onCommit(uint blockNumber, bytes32, string memory) internal {
        require(msg.sender == operator, "error_notPermitted");
        blockTimestamp[blockNumber] = now; // solium-disable-line security/no-block-members
    }

    /**
     * Called from BalanceVerifier.prove.
     * Prove can be called directly to withdraw less than the whole share,
     *   or just "cement" the earnings so far into root chain even without withdrawing.
     * Missing balance test is an extra layer of defense against fraudulent operator who tries to steal ALL tokens.
     * If any member can exit within freeze period, that fraudulent commit will fail.
     * Only earnings that have been committed longer than blockFreezeSeconds ago can be proven, see `onCommit`.
     * See README under "Threat model" for discussion on safety of using "now".
     * @param blockNumber after which balances were submitted in {onCommit}
     * @param account whose earnings were successfully proven and updated
     * @param newEarnings the updated total lifetime earnings
     */
    function onVerifySuccess(uint blockNumber, address account, uint newEarnings) internal {
        uint blockFreezeStart = blockTimestamp[blockNumber];
        require(now > blockFreezeStart + blockFreezeSeconds, "error_frozen"); // solium-disable-line security/no-block-members
        require(earnings[account] < newEarnings, "error_oldEarnings");
        totalProven = totalProven.add(newEarnings).sub(earnings[account]);
        require(totalProven.sub(totalWithdrawn) <= token.balanceOf(address(this)), "error_missingBalance");
        earnings[account] = newEarnings;
    }

    /**
     * Prove and withdraw the whole revenue share from sidechain in one transaction.
     * @param blockNumber of the commit that contains the earnings to verify
     * @param totalEarnings in the off-chain balance book
     * @param proof list of hashes to prove the totalEarnings
     */
    function withdrawAll(uint blockNumber, uint totalEarnings, bytes32[] calldata proof) external {
        withdrawAllFor(msg.sender, blockNumber, totalEarnings, proof);
    }

    /**
     * Prove and withdraw the whole revenue share on behalf of someone else.
     * Validator needs to exit those it's watching out for, in case
     *   it detects Operator malfunctioning.
     * @param recipient the address we're proving and withdrawing to
     * @param blockNumber of the commit that contains the earnings to verify
     * @param totalEarnings in the off-chain balance book
     * @param proof list of hashes to prove the totalEarnings
     */
    function withdrawAllFor(address recipient, uint blockNumber, uint totalEarnings, bytes32[] memory proof) public {
        prove(blockNumber, recipient, totalEarnings, proof);
        uint withdrawable = totalEarnings.sub(withdrawn[recipient]);
        withdrawFor(recipient, withdrawable);
    }

    /**
     * Prove and "donate withdraw" function that allows you to prove and transfer
     *   your earnings to a another address in one transaction.
     * @param recipient the address the tokens will be sent to (instead of msg.sender)
     * @param blockNumber of the commit that contains the earnings to verify
     * @param totalEarnings in the off-chain balance book
     * @param proof list of hashes to prove the totalEarnings
     */
    function withdrawAllTo(address recipient, uint blockNumber, uint totalEarnings, bytes32[] calldata proof) external {
        prove(blockNumber, msg.sender, totalEarnings, proof);
        uint withdrawable = totalEarnings.sub(withdrawn[msg.sender]);
        withdrawTo(recipient, withdrawable);
    }

    /**
     * Prove and do an "unlimited donate withdraw" on behalf of someone else, to an address they've specified.
     * Sponsored withdraw is paid by e.g. admin, but target account could be whatever the member specifies.
     * The signature gives a "blank cheque" for admin to withdraw all tokens to `recipient` in the future,
     *   and it's valid until next withdraw (and so can be nullified by withdrawing any amount).
     * A new signature needs to be obtained for each subsequent future withdraw.
     * @param recipient the address the tokens will be sent to (instead of `msg.sender`)
     * @param signer whose earnings are being withdrawn
     * @param signature from the community member, see `signatureIsValid` how signature generated for unlimited amount
     * @param blockNumber of the commit that contains the earnings to verify
     * @param totalEarnings in the off-chain balance book
     * @param proof list of hashes to prove the totalEarnings
     */
    function withdrawAllToSigned(
        address recipient,
        address signer, bytes calldata signature,                       // signature arguments
        uint blockNumber, uint totalEarnings, bytes32[] calldata proof  // proof arguments
    )
        external
    {
        require(signatureIsValid(recipient, signer, 0, signature), "error_badSignature");
        prove(blockNumber, signer, totalEarnings, proof);
        uint withdrawable = totalEarnings.sub(withdrawn[signer]);
        _withdraw(recipient, signer, withdrawable);
    }

    /**
     * Prove and do a "donate withdraw" on behalf of someone else, to an address they've specified.
     * Sponsored withdraw is paid by e.g. admin, but target account could be whatever the member specifies.
     * The signature is valid only for given amount of tokens that may be different from maximum withdrawable tokens.
     * @param recipient the address the tokens will be sent to (instead of msg.sender)
     * @param signer whose earnings are being withdrawn
     * @param amount of tokens to withdraw
     * @param signature from the community member, see `signatureIsValid` how it's generated
     * @param blockNumber of the commit that contains the earnings to verify
     * @param totalEarnings in the off-chain balance book
     * @param proof list of hashes to prove the totalEarnings
     */
    function proveAndWithdrawToSigned(
        address recipient,
        address signer, uint amount, bytes calldata signature,          // signature arguments
        uint blockNumber, uint totalEarnings, bytes32[] calldata proof  // proof arguments
    )
        external
    {
        require(signatureIsValid(recipient, signer, amount, signature), "error_badSignature");
        prove(blockNumber, signer, totalEarnings, proof);
        _withdraw(recipient, signer, amount);
    }

    /**
     * Withdraw a specified amount of your own proven earnings (see `function prove`).
     * @param amount of tokens to withdraw
     */
    function withdraw(uint amount) public {
        _withdraw(msg.sender, msg.sender, amount);
    }

    /**
     * Withdraw a specified amount on behalf of someone else.
     * Validator needs to exit those it's watching out for, in case it detects Operator malfunctioning.
     * @param recipient whose tokens will be withdrawn (instead of msg.sender)
     * @param amount of tokens to withdraw
     */
    function withdrawFor(address recipient, uint amount) public {
        _withdraw(recipient, recipient, amount);
    }

    /**
     * "Donate withdraw":
     * Withdraw and transfer proven earnings to a another address in one transaction,
     *   instead of withdrawing and then transfering the tokens.
     * @param recipient the address the tokens will be sent to (instead of `msg.sender`)
     * @param amount of tokens to withdraw
     */
    function withdrawTo(address recipient, uint amount) public {
        _withdraw(recipient, msg.sender, amount);
    }

    /**
     * Signed "donate withdraw":
     * Withdraw and transfer proven earnings to a third address on behalf of someone else.
     * Sponsored withdraw is paid by e.g. admin, but target account could be whatever the member specifies.
     * @param recipient of the tokens
     * @param signer whose earnings are being withdrawn
     * @param amount how much is authorized for withdrawing by the signature
     * @param signature from the community member, see `signatureIsValid` how it's generated
     */
    function withdrawToSigned(address recipient, address signer, uint amount, bytes memory signature) public {
        require(signatureIsValid(recipient, signer, amount, signature), "error_badSignature");
        _withdraw(recipient, signer, amount);
    }

    /**
     * Execute token withdrawal into specified recipient address from specified member account.
     * To prevent "bank runs", it is up to the sidechain implementation to make sure that always:
     * `sum of committed earnings <= token.balanceOf(this) + totalWithdrawn`.
     * Smart contract can't verify that, because it can't see inside the commit hash.
     * @param recipient of the tokens
     * @param account whose earnings are being debited
     * @param amount of tokens that is sent out
     */
    function _withdraw(address recipient, address account, uint amount) internal {
        require(amount > 0, "error_zeroWithdraw");
        uint w = withdrawn[account].add(amount);
        require(w <= earnings[account], "error_overdraft");
        withdrawn[account] = w;
        totalWithdrawn = totalWithdrawn.add(amount);
        require(token.transfer(recipient, amount), "error_transfer");
    }

    /**
     * Check signature from a member authorizing withdrawing its earnings to another account.
     * Throws if the signature is badly formatted or doesn't match the given signer and amount.
     * Signature has parts the act as replay protection:
     * 1) `address(this)`: signature can't be used for other contracts;
     * 2) `withdrawn[signer]`: signature only works once (for unspecified amount), and can be "cancelled" by sending a withdraw tx.
     * Generated in Javascript with: `web3.eth.accounts.sign(recipientAddress + amount.toString(16, 64) + contractAddress.slice(2) + withdrawnTokens.toString(16, 64), signerPrivateKey)`,
     * or for unlimited amount: `web3.eth.accounts.sign(recipientAddress + "0".repeat(64) + contractAddress.slice(2) + withdrawnTokens.toString(16, 64), signerPrivateKey)`.
     * @param recipient of the tokens
     * @param signer whose earnings are being withdrawn
     * @param amount how much is authorized for withdraw, or zero for unlimited (withdrawAll)
     * @param signature byte array from `web3.eth.accounts.sign`
     * @return true iff signer of the authorization (member whose earnings are going to be withdrawn) matches the signature
     */
    function signatureIsValid(address recipient, address signer, uint amount, bytes memory signature) public view returns (bool isValid) {
        require(signature.length == 65, "error_badSignatureLength");

        bytes32 r; bytes32 s; uint8 v;
        assembly {      // solium-disable-line security/no-inline-assembly
            r := mload(add(signature, 32))
            s := mload(add(signature, 64))
            v := byte(0, mload(add(signature, 96)))
        }
        if (v < 27) {
            v += 27;
        }
        require(v == 27 || v == 28, "error_badSignatureVersion");

        // When changing the message, remember to double-check that message length is correct!
        bytes32 messageHash = keccak256(abi.encodePacked(
            "\x19Ethereum Signed Message:\n104", recipient, amount, address(this), withdrawn[signer]));
        address calculatedSigner = ecrecover(messageHash, v, r, s);

        return calculatedSigner == signer;
    }
}


contract DataunionVault is Monoplasma {

    string public joinPartStream;

    /** Server version. This must be kept in sync with src/server.js */
    uint public version = 1;

    constructor(address operator, string memory joinPartStreamId, address tokenAddress, uint blockFreezePeriodSeconds, uint adminFeeFraction)
    Monoplasma(tokenAddress, blockFreezePeriodSeconds, adminFeeFraction) public {
        setOperator(operator);
        joinPartStream = joinPartStreamId;
    }
}

Read Contract

adminFee 0xa0be06f9 → uint256
blockFreezeSeconds 0x71b15013 → uint256
blockTimestamp 0x73d8903b → uint256
calculateRootHash 0x6dd4a7c9 → bytes32
committedHash 0xe1a9888e → bytes32
earnings 0x543fd313 → uint256
joinPartStream 0x4beb62b7 → string
operator 0x570ca735 → address
owner 0x8da5cb5b → address
pendingOwner 0xe30c3978 → address
proofIsCorrect 0xcb9b51c8 → bool
signatureIsValid 0xa2d3cf4b → bool
token 0xfc0c546a → address
totalProven 0xb74d784e → uint256
totalWithdrawn 0x4b319713 → uint256
version 0x54fd4d50 → uint256
withdrawn 0x6ef61092 → uint256

Write Contract 15 functions

These functions modify contract state and require a wallet transaction to execute.

claimOwnership 0x4e71e0c8
No parameters
commit 0xc3ac610d
uint256 blockNumber
bytes32 rootHash
string ipfsHash
prove 0x32ef2b24
uint256 blockNumber
address account
uint256 balance
bytes32[] proof
proveAndWithdrawToSigned 0xa0b9d101
address recipient
address signer
uint256 amount
bytes signature
uint256 blockNumber
uint256 totalEarnings
bytes32[] proof
setAdminFee 0x8beb60b6
uint256 newAdminFee
setOperator 0xb3ab15fb
address newOperator
transferOwnership 0xf2fde38b
address newOwner
withdraw 0x2e1a7d4d
uint256 amount
withdrawAll 0x4942e4cf
uint256 blockNumber
uint256 totalEarnings
bytes32[] proof
withdrawAllFor 0x6c7a9d24
address recipient
uint256 blockNumber
uint256 totalEarnings
bytes32[] proof
withdrawAllTo 0x270231cc
address recipient
uint256 blockNumber
uint256 totalEarnings
bytes32[] proof
withdrawAllToSigned 0xb619d3bd
address recipient
address signer
bytes signature
uint256 blockNumber
uint256 totalEarnings
bytes32[] proof
withdrawFor 0xdb518db2
address recipient
uint256 amount
withdrawTo 0x205c2878
address recipient
uint256 amount
withdrawToSigned 0xadc4c874
address recipient
address signer
uint256 amount
bytes signature

Recent Transactions

This address has 1 on-chain transactions, but only 1.2% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →