{"file_path":"contracts/Savings.sol","creation_status":"success","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport './utils/ERC20.sol';\nimport './interface/IFrankencoin.sol';\nimport './interface/IReserve.sol';\nimport './Leadrate.sol';\n\n/**\n * @title Savings\n *\n * Module to enable savings based on a Leadrate (\"Leitzins\") module.\n *\n * As the interest rate changes, the speed at which 'ticks' are accumulated is\n * adjusted. The ticks counter serves as the basis for calculating the interest\n * due for the individual accoutns.\n *\n * The saved ZCHF are subject to a lockup of up to 3 days and only start to yield\n * an interest after the lockup ended. The purpose of this lockup is to discourage\n * short-term holdings and to avoid paying interest to transactional accounts.\n * Transactional accounts typically do not need an incentive to hold Frankencoins.\n */\ncontract Savings is Leadrate {\n\tuint64 public immutable INTEREST_DELAY = uint64(3 days);\n\n\tIERC20 public immutable zchf;\n\n\tmapping(address => Account) public savings;\n\n\tstruct Account {\n\t\tuint192 saved;\n\t\tuint64 ticks;\n\t}\n\n\tevent Saved(address indexed account, uint192 amount);\n\tevent InterestCollected(address indexed account, uint256 interest);\n\tevent Withdrawn(address indexed account, uint192 amount);\n\n\terror FundsLocked(uint40 remainingSeconds);\n\n\t// The module is considered disabled if the interest is zero or about to become zero within three days.\n\terror ModuleDisabled();\n\n\tconstructor(IFrankencoin zchf_, uint24 initialRatePPM) Leadrate(IReserve(zchf_.reserve()), initialRatePPM) {\n\t\tzchf = IERC20(zchf_);\n\t}\n\n\t/**\n\t * Shortcut for refreshBalance(msg.sender)\n\t */\n\tfunction refreshMyBalance() public returns (uint192) {\n\t\treturn refreshBalance(msg.sender);\n\t}\n\n\t/**\n\t * Collects the accrued interest and adds it to the account.\n\t *\n\t * It can be beneficial to do so every now and then in order to start collecting\n\t * interest on the accrued interest.\n\t */\n\tfunction refreshBalance(address owner) public returns (uint192) {\n\t\treturn refresh(owner).saved;\n\t}\n\n\tfunction refresh(address accountOwner) internal returns (Account storage) {\n\t\tAccount storage account = savings[accountOwner];\n\t\tuint64 ticks = currentTicks();\n\t\tif (ticks > account.ticks) {\n\t\t\tuint192 earnedInterest = calculateInterest(account, ticks);\n\t\t\tif (earnedInterest > 0) {\n\t\t\t\t// collect interest as you go and trigger accounting event\n\t\t\t\t(IFrankencoin(address(zchf))).coverLoss(address(this), earnedInterest);\n\t\t\t\taccount.saved += earnedInterest;\n\t\t\t\temit InterestCollected(accountOwner, earnedInterest);\n\t\t\t}\n\t\t\taccount.ticks = ticks;\n\t\t}\n\t\treturn account;\n\t}\n\n\tfunction accruedInterest(address accountOwner) public view returns (uint192) {\n\t\treturn accruedInterest(accountOwner, block.timestamp);\n\t}\n\n\tfunction accruedInterest(address accountOwner, uint256 timestamp) public view returns (uint192) {\n\t\tAccount memory account = savings[accountOwner];\n\t\treturn calculateInterest(account, ticks(timestamp));\n\t}\n\n\tfunction calculateInterest(Account memory account, uint64 ticks) public view returns (uint192) {\n\t\tif (ticks <= account.ticks || account.ticks == 0) {\n\t\t\treturn 0;\n\t\t} else {\n\t\t\tuint192 earnedInterest = uint192((uint256(ticks - account.ticks) * account.saved) / 1000000 / 365 days);\n\t\t\tuint256 equity = IFrankencoin(address(zchf)).equity();\n\t\t\tif (earnedInterest > equity) {\n\t\t\t\treturn uint192(equity); // save conversion as equity is smaller than uint192 earnedInterest\n\t\t\t} else {\n\t\t\t\treturn earnedInterest;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Save 'amount'.\n\t */\n\tfunction save(uint192 amount) public {\n\t\tsave(msg.sender, amount);\n\t}\n\n\tfunction adjust(uint192 targetAmount) public {\n\t\tAccount storage balance = refresh(msg.sender);\n\t\tif (balance.saved < targetAmount) {\n\t\t\tsave(targetAmount - balance.saved);\n\t\t} else if (balance.saved > targetAmount) {\n\t\t\twithdraw(msg.sender, balance.saved - targetAmount);\n\t\t}\n\t}\n\n\t/**\n\t * Send 'amount' to the account of the provided owner.\n\t * The funds sent to the account are locked for a while, depending on how much already is in there.\n\t */\n\tfunction save(address owner, uint192 amount) public {\n\t\tif (currentRatePPM == 0) revert ModuleDisabled();\n\t\tif (nextRatePPM == 0 && (nextChange <= block.timestamp + INTEREST_DELAY)) revert ModuleDisabled();\n\t\tAccount storage balance = refresh(owner);\n\t\tzchf.transferFrom(msg.sender, address(this), amount);\n\t\tuint64 ticks = currentTicks();\n\t\tassert(balance.ticks >= ticks);\n\t\tuint256 saved = balance.saved;\n\t\tuint64 weightedAverage = uint64(\n\t\t\t(saved * (balance.ticks - ticks) + uint256(amount) * currentRatePPM * INTEREST_DELAY) / (saved + amount)\n\t\t);\n\t\tbalance.saved += amount;\n\t\tbalance.ticks = ticks + weightedAverage;\n\t\temit Saved(owner, amount);\n\t}\n\n\t/**\n\t * Withdraw up to 'amount' to the target address.\n\t * When trying to withdraw more than available, all that is available is withdrawn.\n\t * Returns the acutally transferred amount.\n\t *\n\t * Fails if the funds in the account have not been in the account for long enough.\n\t */\n\tfunction withdraw(address target, uint192 amount) public returns (uint256) {\n\t\tAccount storage account = refresh(msg.sender);\n\t\tif (account.ticks > currentTicks()) {\n\t\t\trevert FundsLocked(uint40(account.ticks - currentTicks()) / currentRatePPM);\n\t\t} else if (amount >= account.saved) {\n\t\t\tamount = account.saved;\n\t\t\tdelete savings[msg.sender];\n\t\t} else {\n\t\t\taccount.saved -= amount;\n\t\t}\n\t\tzchf.transfer(target, amount);\n\t\temit Withdrawn(msg.sender, amount);\n\t\treturn amount;\n\t}\n}\n","deployed_bytecode":"0x608060405234801561001057600080fd5b506004361061012c5760003560e01c806385bd12d1116100ad578063b77cd1c711610071578063b77cd1c71461032f578063bdc6515214610342578063c198361014610355578063c4d4803a14610368578063fd449c141461038f57600080fd5b806385bd12d11461029f57806391a0ac6a146102a7578063a696399d146102e6578063b079f163146102f9578063b6f83c171461030157600080fd5b80634df61d82116100f45780634df61d82146102075780634e65037e14610246578063534cb30d1461024e57806377267ec3146102615780637915ce201461028c57600080fd5b806306a7b376146101315780631791dc5e146101595780631de252e41461017a5780631f7cdd5f1461018f5780632e4b20ab146101f1575b600080fd5b6000546101409062ffffff1681565b60405162ffffff90911681526020015b60405180910390f35b61016c610167366004610fd7565b6103a2565b604051908152602001610150565b61018d61018836600461100a565b610599565b005b6101ca61019d366004611099565b6001602052600090815260409020546001600160c01b03811690600160c01b90046001600160401b031682565b604080516001600160c01b0390931683526001600160401b03909116602083015201610150565b600054610140906301000000900462ffffff1681565b61022e7f000000000000000000000000000000000000000000000000000000000003f48081565b6040516001600160401b039091168152602001610150565b61018d6106c1565b61022e61025c3660046110bb565b61082b565b61027461026f366004611099565b610875565b6040516001600160c01b039091168152602001610150565b61027461029a3660046110eb565b610881565b6102746109c2565b6102ce7f0000000000000000000000001ba26788dfde592fec8bcb0eaff472a42be341b281565b6040516001600160a01b039091168152602001610150565b6102746102f436600461116b565b6109d2565b61022e610a2e565b60005461031990600160301b900464ffffffffff1681565b60405164ffffffffff9091168152602001610150565b61027461033d366004611099565b610a39565b61018d610350366004611195565b610a54565b61018d610363366004610fd7565b610aca565b6102ce7f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb81565b61018d61039d366004611195565b610dca565b6000806103ae33610dd7565b90506103b8610a2e565b81546001600160401b03918216600160c01b90910490911611156104335760005462ffffff166103e6610a2e565b82546104029190600160c01b90046001600160401b03166111c6565b61040c91906111fc565b604051630ad679f360e41b815264ffffffffff909116600482015260240160405180910390fd5b80546001600160c01b039081169084161061046a578054336000908152600160205260408120556001600160c01b031692506104ac565b8054839082906000906104879084906001600160c01b0316611220565b92506101000a8154816001600160c01b0302191690836001600160c01b031602179055505b60405163a9059cbb60e01b81526001600160a01b0385811660048301526001600160c01b03851660248301527f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb169063a9059cbb906044016020604051808303816000875af1158015610523573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105479190611240565b506040516001600160c01b038416815233907f47cf194f5e559cca0413017d38814a7843cc6f3052bc43c8085938774ae581519060200160405180910390a250506001600160c01b0381165b92915050565b60405163352e3a8360e01b81526001600160a01b037f0000000000000000000000001ba26788dfde592fec8bcb0eaff472a42be341b2169063352e3a83906105e990339086908690600401611262565b60006040518083038186803b15801561060157600080fd5b505afa158015610615573d6000803e3d6000fd5b50506000805465ffffff0000001916630100000062ffffff8816021790555061064390504262093a806112bc565b600080546affffffffff0000000000001916600160301b64ffffffffff93841681029190911791829055604080513381526301000000840462ffffff16602082015291909204909216908201527e4964f2e48bd8a460fb41883098593956d335f36fdded4ca9cbac9252d72b2f9060600160405180910390a1505050565b60005462ffffff6301000000820481169116036106f15760405163147fde5f60e31b815260040160405180910390fd5b600054429064ffffffffff600160301b90910481169082161015610728576040516301b3782160e21b815260040160405180910390fd5b60005462ffffff81169061074a90600160581b900464ffffffffff16836112cf565b61075491906112ed565b64ffffffffff16600060108282829054906101000a90046001600160401b031661077e919061130e565b82546101009290920a6001600160401b038181021990931691909216919091021790555060008054630100000064ffffffffff60581b198216600160581b64ffffffffff8616029081179190910462ffffff166fffffffffff0000000000000000ffffff1990921662ffffff19919091161781179091556040519081527fd76dfbd4c35cffe2a846b6488bc677c511aa4337e1551f3a360427ac7a78de7b9060200160405180910390a150565b6000805462ffffff81169061084e90600160581b900464ffffffffff16846111c6565b610858919061132e565b6000546105939190600160801b90046001600160401b031661130e565b600061059382426109d2565b600082602001516001600160401b0316826001600160401b03161115806108b3575060208301516001600160401b0316155b156108c057506000610593565b60006301e13380620f424085600001516001600160c01b03168660200151866108e991906111c6565b6001600160401b03166108fc9190611351565b6109069190611368565b6109109190611368565b905060007f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb6001600160a01b03166391a0ac6a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610972573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610996919061137c565b905080826001600160c01b031611156109b25791506105939050565b509050610593565b505092915050565b60006109cd33610a39565b905090565b6001600160a01b03821660009081526001602090815260408083208151808301909252546001600160c01b0381168252600160c01b90046001600160401b031691810191909152610a268161029a8561082b565b949350505050565b60006109cd4261082b565b6000610a4482610dd7565b546001600160c01b031692915050565b6000610a5f33610dd7565b80549091506001600160c01b0380841691161015610a94578054610a909061039d906001600160c01b031684611220565b5050565b80546001600160c01b0380841691161115610a90578054610ac59033906101679085906001600160c01b0316611220565b505050565b6000805462ffffff169003610af257604051630dbfe5fd60e31b815260040160405180910390fd5b6000546301000000900462ffffff16158015610b505750610b3c6001600160401b037f000000000000000000000000000000000000000000000000000000000003f48016426112bc565b600054600160301b900464ffffffffff1611155b15610b6e57604051630dbfe5fd60e31b815260040160405180910390fd5b6000610b7983610dd7565b6040516323b872dd60e01b81523360048201523060248201526001600160c01b03841660448201529091507f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb6001600160a01b0316906323b872dd906064016020604051808303816000875af1158015610bf7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c1b9190611240565b506000610c26610a2e565b82549091506001600160401b03808316600160c01b909204161015610c4d57610c4d611395565b81546001600160c01b0390811690600090610c6a908616836112bc565b6000546001600160401b037f000000000000000000000000000000000000000000000000000000000003f4801690610cb09062ffffff166001600160c01b038916611351565b610cba9190611351565b8554610cd7908690600160c01b90046001600160401b03166111c6565b610cea906001600160401b031685611351565b610cf491906112bc565b610cfe9190611368565b845490915085908590600090610d1e9084906001600160c01b03166113ab565b92506101000a8154816001600160c01b0302191690836001600160c01b031602179055508083610d4e919061130e565b84546001600160401b0391909116600160c01b026001600160c01b039091161784556040516001600160a01b038716907ff195ce54b48d5147da31c1fc525c8828b8836088b505a329e5de2b35da6731e290610dba9088906001600160c01b0391909116815260200190565b60405180910390a2505050505050565b610dd43382610aca565b50565b6001600160a01b038116600090815260016020526040812081610df8610a2e565b82549091506001600160401b03600160c01b90910481169082161115610f9d576040805180820190915282546001600160c01b0381168252600160c01b90046001600160401b03166020820152600090610e529083610881565b90506001600160c01b03811615610f7d57604051631a3f4bd360e31b81523060048201526001600160c01b03821660248201527f000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb6001600160a01b03169063d1fa5e9890604401600060405180830381600087803b158015610ed357600080fd5b505af1158015610ee7573d6000803e3d6000fd5b50508454839250859150600090610f089084906001600160c01b03166113ab565b92506101000a8154816001600160c01b0302191690836001600160c01b03160217905550846001600160a01b03167fed3bc1b58411ea8e764b3ef8fa6b654877bc1e796a5a1a2373dfc8f1c0e0ea4882604051610f7491906001600160c01b0391909116815260200190565b60405180910390a25b5081546001600160c01b0316600160c01b6001600160401b038316021782555b5092915050565b80356001600160a01b0381168114610fbb57600080fd5b919050565b80356001600160c01b0381168114610fbb57600080fd5b60008060408385031215610fea57600080fd5b610ff383610fa4565b915061100160208401610fc0565b90509250929050565b60008060006040848603121561101f57600080fd5b833562ffffff8116811461103257600080fd5b925060208401356001600160401b038082111561104e57600080fd5b818601915086601f83011261106257600080fd5b81358181111561107157600080fd5b8760208260051b850101111561108657600080fd5b6020830194508093505050509250925092565b6000602082840312156110ab57600080fd5b6110b482610fa4565b9392505050565b6000602082840312156110cd57600080fd5b5035919050565b80356001600160401b0381168114610fbb57600080fd5b60008082840360608112156110ff57600080fd5b604081121561110d57600080fd5b50604051604081018181106001600160401b038211171561113e57634e487b7160e01b600052604160045260246000fd5b60405261114a84610fc0565b8152611158602085016110d4565b60208201529150611001604084016110d4565b6000806040838503121561117e57600080fd5b61118783610fa4565b946020939093013593505050565b6000602082840312156111a757600080fd5b6110b482610fc0565b634e487b7160e01b600052601160045260246000fd5b6001600160401b03828116828216039080821115610f9d57610f9d6111b0565b634e487b7160e01b600052601260045260246000fd5b600064ffffffffff80841680611214576112146111e6565b92169190910492915050565b6001600160c01b03828116828216039080821115610f9d57610f9d6111b0565b60006020828403121561125257600080fd5b815180151581146110b457600080fd5b6001600160a01b03848116825260406020808401829052908301849052600091859160608501845b878110156112af578361129c86610fa4565b168252938201939082019060010161128a565b5098975050505050505050565b80820180821115610593576105936111b0565b64ffffffffff828116828216039080821115610f9d57610f9d6111b0565b64ffffffffff8181168382160280821691908281146109ba576109ba6111b0565b6001600160401b03818116838216019080821115610f9d57610f9d6111b0565b6001600160401b038181168382160280821691908281146109ba576109ba6111b0565b8082028115828204841417610593576105936111b0565b600082611377576113776111e6565b500490565b60006020828403121561138e57600080fd5b5051919050565b634e487b7160e01b600052600160045260246000fd5b6001600160c01b03818116838216019080821115610f9d57610f9d6111b056fea2646970667358221220ad0323431b8b61aa03d649336e145fc1724cc9c1dea323eae42c1c8e501a8b1664736f6c63430008140033","optimization_enabled":true,"verified_twin_address_hash":null,"is_verified":true,"compiler_settings":{"evmVersion":"paris","libraries":{},"metadata":{"bytecodeHash":"ipfs","useLiteralContent":true},"optimizer":{"enabled":true,"runs":200},"remappings":[]},"optimization_runs":200,"sourcify_repo_url":"https://repo.sourcify.dev/contracts/full_match/1/0x3BF301B0e2003E75A3e86AB82bD1EFF6A9dFB2aE/","decoded_constructor_args":[["0xB58E61C3098d85632Df34EecfB899A1Ed80921cB",{"internalType":"contract IFrankencoin","name":"zchf_","type":"address"}],["50000",{"internalType":"uint24","name":"initialRatePPM","type":"uint24"}]],"compiler_version":"0.8.20+commit.a1b79de6","is_verified_via_verifier_alliance":false,"verified_at":"2024-11-28T14:58:09.509243Z","implementations":[],"proxy_type":null,"external_libraries":[],"creation_bytecode":"0x60e06040526203f48060a0523480156200001857600080fd5b5060405162001660380380620016608339810160408190526200003b9162000191565b816001600160a01b031663cd3293de6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200007a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000a09190620001d7565b6001600160a01b03811660805260008054600160801b600160c01b03196b010000000000000000000000660100000000000064ffffffffff42811682026affffffffff000000ffffff1962ffffff8a1663010000008102919091166001600160581b031990971696909617861717918204169190910291909116600160581b600160c01b0319919091161790915560405190815282907fd76dfbd4c35cffe2a846b6488bc677c511aa4337e1551f3a360427ac7a78de7b9060200160405180910390a15050506001600160a01b031660c052620001fe565b6001600160a01b03811681146200018e57600080fd5b50565b60008060408385031215620001a557600080fd5b8251620001b28162000178565b602084015190925062ffffff81168114620001cc57600080fd5b809150509250929050565b600060208284031215620001ea57600080fd5b8151620001f78162000178565b9392505050565b60805160a05160c0516114016200025f6000396000818161036d015281816104da0152818161091601528181610ba60152610e8701526000818161020c01528181610b160152610c770152600081816102ac01526105b001526114016000f3fe608060405234801561001057600080fd5b506004361061012c5760003560e01c806385bd12d1116100ad578063b77cd1c711610071578063b77cd1c71461032f578063bdc6515214610342578063c198361014610355578063c4d4803a14610368578063fd449c141461038f57600080fd5b806385bd12d11461029f57806391a0ac6a146102a7578063a696399d146102e6578063b079f163146102f9578063b6f83c171461030157600080fd5b80634df61d82116100f45780634df61d82146102075780634e65037e14610246578063534cb30d1461024e57806377267ec3146102615780637915ce201461028c57600080fd5b806306a7b376146101315780631791dc5e146101595780631de252e41461017a5780631f7cdd5f1461018f5780632e4b20ab146101f1575b600080fd5b6000546101409062ffffff1681565b60405162ffffff90911681526020015b60405180910390f35b61016c610167366004610fd7565b6103a2565b604051908152602001610150565b61018d61018836600461100a565b610599565b005b6101ca61019d366004611099565b6001602052600090815260409020546001600160c01b03811690600160c01b90046001600160401b031682565b604080516001600160c01b0390931683526001600160401b03909116602083015201610150565b600054610140906301000000900462ffffff1681565b61022e7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160401b039091168152602001610150565b61018d6106c1565b61022e61025c3660046110bb565b61082b565b61027461026f366004611099565b610875565b6040516001600160c01b039091168152602001610150565b61027461029a3660046110eb565b610881565b6102746109c2565b6102ce7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610150565b6102746102f436600461116b565b6109d2565b61022e610a2e565b60005461031990600160301b900464ffffffffff1681565b60405164ffffffffff9091168152602001610150565b61027461033d366004611099565b610a39565b61018d610350366004611195565b610a54565b61018d610363366004610fd7565b610aca565b6102ce7f000000000000000000000000000000000000000000000000000000000000000081565b61018d61039d366004611195565b610dca565b6000806103ae33610dd7565b90506103b8610a2e565b81546001600160401b03918216600160c01b90910490911611156104335760005462ffffff166103e6610a2e565b82546104029190600160c01b90046001600160401b03166111c6565b61040c91906111fc565b604051630ad679f360e41b815264ffffffffff909116600482015260240160405180910390fd5b80546001600160c01b039081169084161061046a578054336000908152600160205260408120556001600160c01b031692506104ac565b8054839082906000906104879084906001600160c01b0316611220565b92506101000a8154816001600160c01b0302191690836001600160c01b031602179055505b60405163a9059cbb60e01b81526001600160a01b0385811660048301526001600160c01b03851660248301527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af1158015610523573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105479190611240565b506040516001600160c01b038416815233907f47cf194f5e559cca0413017d38814a7843cc6f3052bc43c8085938774ae581519060200160405180910390a250506001600160c01b0381165b92915050565b60405163352e3a8360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063352e3a83906105e990339086908690600401611262565b60006040518083038186803b15801561060157600080fd5b505afa158015610615573d6000803e3d6000fd5b50506000805465ffffff0000001916630100000062ffffff8816021790555061064390504262093a806112bc565b600080546affffffffff0000000000001916600160301b64ffffffffff93841681029190911791829055604080513381526301000000840462ffffff16602082015291909204909216908201527e4964f2e48bd8a460fb41883098593956d335f36fdded4ca9cbac9252d72b2f9060600160405180910390a1505050565b60005462ffffff6301000000820481169116036106f15760405163147fde5f60e31b815260040160405180910390fd5b600054429064ffffffffff600160301b90910481169082161015610728576040516301b3782160e21b815260040160405180910390fd5b60005462ffffff81169061074a90600160581b900464ffffffffff16836112cf565b61075491906112ed565b64ffffffffff16600060108282829054906101000a90046001600160401b031661077e919061130e565b82546101009290920a6001600160401b038181021990931691909216919091021790555060008054630100000064ffffffffff60581b198216600160581b64ffffffffff8616029081179190910462ffffff166fffffffffff0000000000000000ffffff1990921662ffffff19919091161781179091556040519081527fd76dfbd4c35cffe2a846b6488bc677c511aa4337e1551f3a360427ac7a78de7b9060200160405180910390a150565b6000805462ffffff81169061084e90600160581b900464ffffffffff16846111c6565b610858919061132e565b6000546105939190600160801b90046001600160401b031661130e565b600061059382426109d2565b600082602001516001600160401b0316826001600160401b03161115806108b3575060208301516001600160401b0316155b156108c057506000610593565b60006301e13380620f424085600001516001600160c01b03168660200151866108e991906111c6565b6001600160401b03166108fc9190611351565b6109069190611368565b6109109190611368565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166391a0ac6a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610972573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610996919061137c565b905080826001600160c01b031611156109b25791506105939050565b509050610593565b505092915050565b60006109cd33610a39565b905090565b6001600160a01b03821660009081526001602090815260408083208151808301909252546001600160c01b0381168252600160c01b90046001600160401b031691810191909152610a268161029a8561082b565b949350505050565b60006109cd4261082b565b6000610a4482610dd7565b546001600160c01b031692915050565b6000610a5f33610dd7565b80549091506001600160c01b0380841691161015610a94578054610a909061039d906001600160c01b031684611220565b5050565b80546001600160c01b0380841691161115610a90578054610ac59033906101679085906001600160c01b0316611220565b505050565b6000805462ffffff169003610af257604051630dbfe5fd60e31b815260040160405180910390fd5b6000546301000000900462ffffff16158015610b505750610b3c6001600160401b037f000000000000000000000000000000000000000000000000000000000000000016426112bc565b600054600160301b900464ffffffffff1611155b15610b6e57604051630dbfe5fd60e31b815260040160405180910390fd5b6000610b7983610dd7565b6040516323b872dd60e01b81523360048201523060248201526001600160c01b03841660448201529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906323b872dd906064016020604051808303816000875af1158015610bf7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c1b9190611240565b506000610c26610a2e565b82549091506001600160401b03808316600160c01b909204161015610c4d57610c4d611395565b81546001600160c01b0390811690600090610c6a908616836112bc565b6000546001600160401b037f00000000000000000000000000000000000000000000000000000000000000001690610cb09062ffffff166001600160c01b038916611351565b610cba9190611351565b8554610cd7908690600160c01b90046001600160401b03166111c6565b610cea906001600160401b031685611351565b610cf491906112bc565b610cfe9190611368565b845490915085908590600090610d1e9084906001600160c01b03166113ab565b92506101000a8154816001600160c01b0302191690836001600160c01b031602179055508083610d4e919061130e565b84546001600160401b0391909116600160c01b026001600160c01b039091161784556040516001600160a01b038716907ff195ce54b48d5147da31c1fc525c8828b8836088b505a329e5de2b35da6731e290610dba9088906001600160c01b0391909116815260200190565b60405180910390a2505050505050565b610dd43382610aca565b50565b6001600160a01b038116600090815260016020526040812081610df8610a2e565b82549091506001600160401b03600160c01b90910481169082161115610f9d576040805180820190915282546001600160c01b0381168252600160c01b90046001600160401b03166020820152600090610e529083610881565b90506001600160c01b03811615610f7d57604051631a3f4bd360e31b81523060048201526001600160c01b03821660248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063d1fa5e9890604401600060405180830381600087803b158015610ed357600080fd5b505af1158015610ee7573d6000803e3d6000fd5b50508454839250859150600090610f089084906001600160c01b03166113ab565b92506101000a8154816001600160c01b0302191690836001600160c01b03160217905550846001600160a01b03167fed3bc1b58411ea8e764b3ef8fa6b654877bc1e796a5a1a2373dfc8f1c0e0ea4882604051610f7491906001600160c01b0391909116815260200190565b60405180910390a25b5081546001600160c01b0316600160c01b6001600160401b038316021782555b5092915050565b80356001600160a01b0381168114610fbb57600080fd5b919050565b80356001600160c01b0381168114610fbb57600080fd5b60008060408385031215610fea57600080fd5b610ff383610fa4565b915061100160208401610fc0565b90509250929050565b60008060006040848603121561101f57600080fd5b833562ffffff8116811461103257600080fd5b925060208401356001600160401b038082111561104e57600080fd5b818601915086601f83011261106257600080fd5b81358181111561107157600080fd5b8760208260051b850101111561108657600080fd5b6020830194508093505050509250925092565b6000602082840312156110ab57600080fd5b6110b482610fa4565b9392505050565b6000602082840312156110cd57600080fd5b5035919050565b80356001600160401b0381168114610fbb57600080fd5b60008082840360608112156110ff57600080fd5b604081121561110d57600080fd5b50604051604081018181106001600160401b038211171561113e57634e487b7160e01b600052604160045260246000fd5b60405261114a84610fc0565b8152611158602085016110d4565b60208201529150611001604084016110d4565b6000806040838503121561117e57600080fd5b61118783610fa4565b946020939093013593505050565b6000602082840312156111a757600080fd5b6110b482610fc0565b634e487b7160e01b600052601160045260246000fd5b6001600160401b03828116828216039080821115610f9d57610f9d6111b0565b634e487b7160e01b600052601260045260246000fd5b600064ffffffffff80841680611214576112146111e6565b92169190910492915050565b6001600160c01b03828116828216039080821115610f9d57610f9d6111b0565b60006020828403121561125257600080fd5b815180151581146110b457600080fd5b6001600160a01b03848116825260406020808401829052908301849052600091859160608501845b878110156112af578361129c86610fa4565b168252938201939082019060010161128a565b5098975050505050505050565b80820180821115610593576105936111b0565b64ffffffffff828116828216039080821115610f9d57610f9d6111b0565b64ffffffffff8181168382160280821691908281146109ba576109ba6111b0565b6001600160401b03818116838216019080821115610f9d57610f9d6111b0565b6001600160401b038181168382160280821691908281146109ba576109ba6111b0565b8082028115828204841417610593576105936111b0565b600082611377576113776111e6565b500490565b60006020828403121561138e57600080fd5b5051919050565b634e487b7160e01b600052600160045260246000fd5b6001600160c01b03818116838216019080821115610f9d57610f9d6111b056fea2646970667358221220ad0323431b8b61aa03d649336e145fc1724cc9c1dea323eae42c1c8e501a8b1664736f6c63430008140033000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb000000000000000000000000000000000000000000000000000000000000c350","name":"Savings","is_blueprint":false,"license_type":"none","is_fully_verified":true,"is_verified_via_eth_bytecode_db":true,"language":"solidity","evm_version":"paris","can_be_visualized_via_sol2uml":true,"is_verified_via_sourcify":true,"additional_sources":[{"file_path":"contracts/Leadrate.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport './interface/IERC20.sol';\nimport './interface/IFrankencoin.sol';\nimport './interface/IReserve.sol';\n\n/**\n * @title Leadrate (attempt at translating the nicely concise German term 'Leitzins')\n *\n * A module that can provide other modules with the lead interest rate for the system.\n *\n **/\ncontract Leadrate {\n\tIReserve public immutable equity;\n\n\t// the following five variables are less than 256 bit so they should be stored\n\t// in the same slot, making them cheap to access together, right?\n\n\tuint24 public currentRatePPM; // 24 bit allows rates of up to 1670% per year\n\tuint24 public nextRatePPM;\n\tuint40 public nextChange;\n\n\tuint40 private anchorTime; // 40 bits for time in seconds spans up to 1000 human generations\n\tuint64 private ticksAnchor; // in bips * seconds, uint88 allows up to\n\n\tevent RateProposed(address who, uint24 nextRate, uint40 nextChange);\n\tevent RateChanged(uint24 newRate);\n\n\terror NoPendingChange();\n\terror ChangeNotReady();\n\n\tconstructor(IReserve equity_, uint24 initialRatePPM) {\n\t\tequity = equity_;\n\t\tnextRatePPM = initialRatePPM;\n\t\tcurrentRatePPM = initialRatePPM;\n\t\tnextChange = uint40(block.timestamp);\n\t\tanchorTime = nextChange;\n\t\tticksAnchor = 0;\n\t\temit RateChanged(initialRatePPM); // emit for init indexing, if wanted\n\t}\n\n\t/**\n\t * Proposes a new interest rate that will automatically be applied after seven days.\n\t * To cancel a proposal, just overwrite it with a new one proposing the current rate.\n\t */\n\tfunction proposeChange(uint24 newRatePPM_, address[] calldata helpers) external {\n\t\tequity.checkQualified(msg.sender, helpers);\n\t\tnextRatePPM = newRatePPM_;\n\t\tnextChange = uint40(block.timestamp + 7 days);\n\t\temit RateProposed(msg.sender, nextRatePPM, nextChange);\n\t}\n\n\t/**\n\t * Setting a previously proposed interest rate change into force.\n\t */\n\tfunction applyChange() external {\n\t\tif (currentRatePPM == nextRatePPM) revert NoPendingChange();\n\t\tuint40 timeNow = uint40(block.timestamp);\n\t\tif (timeNow < nextChange) revert ChangeNotReady();\n\t\tticksAnchor += (timeNow - anchorTime) * currentRatePPM;\n\t\tanchorTime = timeNow;\n\t\tcurrentRatePPM = nextRatePPM;\n\t\temit RateChanged(currentRatePPM);\n\t}\n\n\t/**\n\t * Total accumulated 'interest ticks' since this contract was deployed.\n\t * One 'tick' is a ppm-second, so one months of 12% annual interest is\n\t *   120000*30*24*3600 = 311040000000 ticks.\n\t * Two months of 6% annual interest would result in the same number of\n\t * ticks. For simplicity, this is linear, so there is no \"interest on interest\".\n\t */\n\tfunction currentTicks() public view returns (uint64) {\n\t\treturn ticks(block.timestamp);\n\t}\n\n\tfunction ticks(uint256 timestamp) public view returns (uint64) {\n\t\treturn ticksAnchor + (uint64(timestamp) - anchorTime) * currentRatePPM;\n\t}\n}\n"},{"file_path":"contracts/interface/IERC20.sol","source_code":"/**\n * SPDX-License-Identifier: MIT\n *\n * Copyright (c) 2016-2019 zOS Global Limited\n *\n */\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP. Does not include\n * the optional functions; to access them see `ERC20Detailed`.\n */\n\ninterface IERC20 {\n    function name() external view returns (string memory);\n\n    function symbol() external view returns (string memory);\n\n    function decimals() external view returns (uint8);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `recipient`.\n     *\n     * Returns always true. Throws error on failure.\n     *\n     * Emits a `Transfer` event.\n     */\n    function transfer(address recipient, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through `transferFrom`. This is\n     * zero by default.\n     *\n     * This value can change when `approve` or `transferFrom` are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * > Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an `Approval` event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `sender` to `recipient` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns always true. Throws error on failure.\n     *\n     * Emits a `Transfer` event.\n     */\n    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to `approve`. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n"},{"file_path":"contracts/interface/IERC677Receiver.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IERC677Receiver {\n    \n    function onTokenTransfer(address from, uint256 amount, bytes calldata data) external returns (bool);\n\n}"},{"file_path":"contracts/interface/IFrankencoin.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./IReserve.sol\";\n\ninterface IFrankencoin is IERC20 {\n    function suggestMinter(address _minter, uint256 _applicationPeriod, uint256 _applicationFee, string calldata _message) external;\n\n    function registerPosition(address position) external;\n\n    function denyMinter(address minter, address[] calldata helpers, string calldata message) external;\n\n    function reserve() external view returns (IReserve);\n\n    function minterReserve() external view returns (uint256);\n\n    function calculateAssignedReserve(uint256 mintedAmount, uint32 _reservePPM) external view returns (uint256);\n\n    function calculateFreedAmount(uint256 amountExcludingReserve, uint32 reservePPM) external view returns (uint256);\n\n    function equity() external view returns (uint256);\n\n    function isMinter(address minter) external view returns (bool);\n\n    function getPositionParent(address position) external view returns (address);\n\n    function mint(address target, uint256 amount) external;\n\n    function mintWithReserve(address target, uint256 amount, uint32 reservePPM, uint32 feePPM) external;\n\n    function burnFrom(address target, uint256 amount) external;\n\n    function burnWithoutReserve(uint256 amountIncludingReserve, uint32 reservePPM) external;\n\n    function burnFromWithReserve(address payer, uint256 targetTotalBurnAmount, uint32 _reservePPM) external returns (uint256);\n\n    function burnWithReserve(uint256 amountExcludingReserve, uint32 reservePPM) external returns (uint256);\n\n    function coverLoss(address source, uint256 amount) external;\n\n    function collectProfits(address source, uint256 _amount) external;\n}\n"},{"file_path":"contracts/interface/IReserve.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\n\ninterface IReserve is IERC20 {\n   function invest(uint256 amount, uint256 expected) external returns (uint256);\n   function checkQualified(address sender, address[] calldata helpers) external view;\n}"},{"file_path":"contracts/utils/ERC20.sol","source_code":"// SPDX-License-Identifier: MIT\n// Copied and adjusted from OpenZeppelin\n// Adjustments:\n// - modifications to support ERC-677\n// - removed require messages to save space\n// - removed unnecessary require statements\n// - removed GSN Context\n// - upgraded to 0.8 to drop SafeMath\n// - let name() and symbol() be implemented by subclass\n// - infinite allowance support, with 2^255 and above considered infinite\n\npragma solidity ^0.8.0;\n\nimport \"../interface/IERC20.sol\";\nimport \"../interface/IERC677Receiver.sol\";\n\n/**\n * @dev Implementation of the `IERC20` interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using `_mint`.\n * For a generic mechanism see `ERC20Mintable`.\n *\n * *For a detailed writeup see our guide [How to implement supply\n * mechanisms](https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226).*\n *\n * We have followed general OpenZeppelin guidelines: functions revert instead\n * of returning `false` on failure. This behavior is nonetheless conventional\n * and does not conflict with the expectations of ERC20 applications.\n *\n * Additionally, an `Approval` event is emitted on calls to `transferFrom`.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n */\n\nabstract contract ERC20 is IERC20 {\n    mapping(address account => uint256 balance) private _balances;\n\n    mapping(address account => mapping(address spender => uint256 allowance)) private _allowances;\n\n    uint256 internal constant INFINITY = (1 << 255);\n\n    uint256 private _totalSupply;\n\n    uint8 public immutable override decimals;\n\n    // Copied from https://github.com/OpenZeppelin/openzeppelin-contracts/pull/4139/files#diff-fa792f7d08644eebc519dac2c29b00a54afc4c6a76b9ef3bba56c8401fe674f6\n    // Indicates an error related to the current balance of a sender. Used in transfers.\n    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);\n    // Indicates a failure with the spender’s allowance. Used in transfers.\n    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);\n\n    constructor(uint8 _decimals) {\n        decimals = _decimals;\n    }\n\n    /**\n     * @dev See `IERC20.totalSupply`.\n     */\n    function totalSupply() public view override returns (uint256) {\n        return _totalSupply;\n    }\n\n    /**\n     * @dev See `IERC20.balanceOf`.\n     */\n    function balanceOf(address account) public view override returns (uint256) {\n        return _balances[account];\n    }\n\n    /**\n     * @dev See `IERC20.transfer`.\n     *\n     * Requirements:\n     *\n     * - `recipient` cannot be the zero address.\n     * - the caller must have a balance of at least `amount`.\n     */\n    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n        _transfer(msg.sender, recipient, amount);\n        return true;\n    }\n\n    /**\n     * @dev See `IERC20.allowance`.\n     */\n    function allowance(address owner, address spender) external view override returns (uint256) {\n        return _allowance(owner, spender);\n    }\n\n    function _allowance(address owner, address spender) internal view virtual returns (uint256) {\n        return _allowances[owner][spender];\n    }\n\n    /**\n     * @dev See `IERC20.approve`.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     */\n    function approve(address spender, uint256 value) external override returns (bool) {\n        _approve(msg.sender, spender, value);\n        return true;\n    }\n\n    /**\n     * @dev See `IERC20.transferFrom`.\n     *\n     * Emits an `Approval` event indicating the updated allowance. This is not\n     * required by the EIP. See the note at the beginning of `ERC20`;\n     *\n     * Requirements:\n     * - `sender` and `recipient` cannot be the zero address.\n     * - `sender` must have a balance of at least `value`.\n     * - the caller must have allowance for `sender`'s tokens of at least\n     * `amount`.\n     */\n    function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) {\n        _transfer(sender, recipient, amount);\n        _useAllowance(sender, msg.sender, amount);\n        return true;\n    }\n\n    function _useAllowance(address owner, address spender, uint256 amount) internal {\n        uint256 currentAllowance = _allowance(owner, spender);\n        if (currentAllowance < INFINITY) {\n            // Only decrease the allowance if it was not set to 'infinite'\n            // Documented in github.com/aktionariat/contracts/blob/master/doc/infiniteallowance.md\n            if (currentAllowance < amount) revert ERC20InsufficientAllowance(owner, currentAllowance, amount);\n            _approve(owner, spender, currentAllowance - amount);\n        }\n    }\n\n    /**\n     * @dev Moves tokens `amount` from `sender` to `recipient`.\n     *\n     * This is internal function is equivalent to `transfer`, and can be used to\n     * e.g. implement automatic token fees, slashing mechanisms, etc.\n     *\n     * Emits a `Transfer` event.\n     *\n     * Requirements:\n     *\n     * - `sender` cannot be the zero address.\n     * - `recipient` cannot be the zero address.\n     * - `sender` must have a balance of at least `amount`.\n     */\n    function _transfer(address sender, address recipient, uint256 amount) internal virtual {\n        require(recipient != address(0));\n\n        _beforeTokenTransfer(sender, recipient, amount);\n        if (_balances[sender] < amount) revert ERC20InsufficientBalance(sender, _balances[sender], amount);\n        _balances[sender] -= amount;\n        _balances[recipient] += amount;\n        emit Transfer(sender, recipient, amount);\n    }\n\n    /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n     * the total supply.\n     *\n     * Emits a `Transfer` event with `from` set to the zero address.\n     *\n     * Requirements\n     *\n     * - `to` cannot be the zero address.\n     */\n    function _mint(address recipient, uint256 amount) internal virtual {\n        require(recipient != address(0));\n\n        _beforeTokenTransfer(address(0), recipient, amount);\n\n        _totalSupply += amount;\n        _balances[recipient] += amount;\n        emit Transfer(address(0), recipient, amount);\n    }\n\n    /**\n     * @dev Destroys `amount` tokens from `account`, reducing the\n     * total supply.\n     *\n     * Emits a `Transfer` event with `to` set to the zero address.\n     */\n    function _burn(address account, uint256 amount) internal virtual {\n        _beforeTokenTransfer(account, address(0), amount);\n\n        _totalSupply -= amount;\n        _balances[account] -= amount;\n        emit Transfer(account, address(0), amount);\n    }\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.\n     *\n     * This is internal function is equivalent to `approve`, and can be used to\n     * e.g. set automatic allowances for certain subsystems, etc.\n     *\n     * Emits an `Approval` event.\n     */\n    function _approve(address owner, address spender, uint256 value) internal {\n        _allowances[owner][spender] = value;\n        emit Approval(owner, spender, value);\n    }\n\n    /**\n     * @dev Hook that is called before any transfer of tokens. This includes\n     * minting and burning.\n     *\n     * Calling conditions:\n     *\n     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n     * will be to transferred to `to`.\n     * - when `from` is zero, `amount` tokens will be minted for `to`.\n     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n     * - `from` and `to` are never both zero.\n     *\n     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n     */\n    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}\n}\n"}],"certified":false,"conflicting_implementations":null,"abi":[{"inputs":[{"internalType":"contract IFrankencoin","name":"zchf_","type":"address"},{"internalType":"uint24","name":"initialRatePPM","type":"uint24"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ChangeNotReady","type":"error"},{"inputs":[{"internalType":"uint40","name":"remainingSeconds","type":"uint40"}],"name":"FundsLocked","type":"error"},{"inputs":[],"name":"ModuleDisabled","type":"error"},{"inputs":[],"name":"NoPendingChange","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"interest","type":"uint256"}],"name":"InterestCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint24","name":"newRate","type":"uint24"}],"name":"RateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"who","type":"address"},{"indexed":false,"internalType":"uint24","name":"nextRate","type":"uint24"},{"indexed":false,"internalType":"uint40","name":"nextChange","type":"uint40"}],"name":"RateProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint192","name":"amount","type":"uint192"}],"name":"Saved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint192","name":"amount","type":"uint192"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"INTEREST_DELAY","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"accountOwner","type":"address"}],"name":"accruedInterest","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"accountOwner","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"accruedInterest","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint192","name":"targetAmount","type":"uint192"}],"name":"adjust","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"applyChange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint192","name":"saved","type":"uint192"},{"internalType":"uint64","name":"ticks","type":"uint64"}],"internalType":"struct Savings.Account","name":"account","type":"tuple"},{"internalType":"uint64","name":"ticks","type":"uint64"}],"name":"calculateInterest","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentRatePPM","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentTicks","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"equity","outputs":[{"internalType":"contract IReserve","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextChange","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextRatePPM","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"newRatePPM_","type":"uint24"},{"internalType":"address[]","name":"helpers","type":"address[]"}],"name":"proposeChange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"refreshBalance","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"refreshMyBalance","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint192","name":"amount","type":"uint192"}],"name":"save","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint192","name":"amount","type":"uint192"}],"name":"save","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"savings","outputs":[{"internalType":"uint192","name":"saved","type":"uint192"},{"internalType":"uint64","name":"ticks","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"ticks","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint192","name":"amount","type":"uint192"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"zchf","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"}],"is_changed_bytecode":false,"is_partially_verified":false,"constructor_args":"0x000000000000000000000000b58e61c3098d85632df34eecfb899a1ed80921cb000000000000000000000000000000000000000000000000000000000000c350"}