├── .gitignore ├── .prettierrc.json ├── .solhint.json ├── LICENSE ├── README.md ├── contracts ├── AdminUpgradeabilityProxy.sol ├── BondDepository.sol ├── ConsensusPool.sol ├── PreSale.sol ├── SAT.sol ├── SATTimelock.sol ├── Staking.sol ├── StakingDistributor.sol ├── StakingHelper.sol ├── StakingWarmup.sol ├── StandardBondingCalculator.sol ├── SynassetsERC20.sol ├── TokenSale.sol ├── Treasury.sol ├── VaultTmp.sol ├── mock │ ├── DAI.sol │ ├── MockSAT.sol │ ├── MockStaking.sol │ ├── MockSynassetsERC20.sol │ ├── MockTokenSale.sol │ ├── MockTreasury.sol │ └── MockUDSC.sol └── sSynassetsERC20.sol ├── docs └── how_it_all_works.jpeg ├── hardhat.config.js ├── package-lock.json ├── package.json ├── scripts ├── ExportTokenSaleData.js ├── deployAll.js ├── deployMockAll.js └── deploySAT.js └── test ├── ConsensusPool-Unit.test.waffle.chai.ethers.js ├── SAT-Unit.test.waffle.chai.ethers.js ├── SATTimelock-Unit.test.waffle.chai.ethers.js ├── TokenSale-Unit.test.waffle.chai.ethers.js └── utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | artifacts/ 3 | cache/ 4 | .idea/ 5 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2 3 | } -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:default", 3 | "plugins": ["prettier"], 4 | "rules": { 5 | "prettier/prettier": "error" 6 | } 7 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Smart Contracts 2 | 3 | ## 🔧 Setting up local development 4 | 5 | Requirements: 6 | - [Node v14](https://nodejs.org/download/release/latest-v14.x/) 7 | - [Git](https://git-scm.com/downloads) 8 | 9 | 10 | Local Setup Steps: 11 | 1. ``git clone https://github.com/synassets/contracts.git `` 12 | 1. Install dependencies: `npm install` 13 | - Installs [Hardhat](https://hardhat.org/getting-started/) and [OpenZepplin](https://docs.openzeppelin.com/contracts/4.x/) dependencies 14 | 1. Compile Solidity: ``npm run compile`` 15 | 16 | ## 🤔 token allocation 17 | 18 | ![High Level Contract Interactions](./docs/how_it_all_works.jpeg) 19 | 20 | ## Contract addresses 21 | 22 | ### Mainnet 23 | 24 | Network: `Polygon` 25 | 26 | - TestSAT: `0x8d727C3D99892b285dD7F0D7268E6cE850532E47` 27 | - TestUSDC: `0x70b04a9AbC8D0D2088a3D0895aD3a6363859592f` 28 | - TokenSale-OG: `0xB1601bF2D7FE00d24ccb4037F5D87b7664b19c7e` 29 | - TokenSale-PUB: `0xdCde2c8CC76f9C40f439e294843D9c7DaB7a2A5e` 30 | - Timelock: `0xd4d8FfaC785d14e2e34afFe92193202c3543c0d8` 31 | -------------------------------------------------------------------------------- /contracts/AdminUpgradeabilityProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.7.5; 4 | 5 | /** 6 | * Utility library of inline functions on addresses 7 | * 8 | * Source https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/v2.1.3/contracts/utils/Address.sol 9 | * This contract is copied here and renamed from the original to avoid clashes in the compiled artifacts 10 | * when the user imports a zos-lib contract (that transitively causes this contract to be compiled and added to the 11 | * build/artifacts folder) as well as the vanilla Address implementation from an openzeppelin version. 12 | */ 13 | library OpenZeppelinUpgradesAddress { 14 | /** 15 | * Returns whether the target address is a contract 16 | * @dev This function will return false if invoked during the constructor of a contract, 17 | * as the code is not actually created until after the constructor finishes. 18 | * @param account address of the account to check 19 | * @return whether the target address is a contract 20 | */ 21 | function isContract(address account) internal view returns (bool) { 22 | uint256 size; 23 | // XXX Currently there is no better way to check if there is a contract in an address 24 | // than to check the size of the code at that address. 25 | // See https://ethereum.stackexchange.com/a/14016/36603 26 | // for more details about how this works. 27 | // TODO Check this again before the Serenity release, because all addresses will be 28 | // contracts then. 29 | // solhint-disable-next-line no-inline-assembly 30 | assembly { size := extcodesize(account) } 31 | return size > 0; 32 | } 33 | } 34 | 35 | /** 36 | * @title Proxy 37 | * @dev Implements delegation of calls to other contracts, with proper 38 | * forwarding of return values and bubbling of failures. 39 | * It defines a fallback function that delegates all calls to the address 40 | * returned by the abstract _implementation() internal function. 41 | */ 42 | abstract contract Proxy { 43 | /** 44 | * @dev Fallback function. 45 | * Implemented entirely in `_fallback`. 46 | */ 47 | fallback () payable external { 48 | _fallback(); 49 | } 50 | 51 | receive () payable external { 52 | _fallback(); 53 | } 54 | 55 | /** 56 | * @return The Address of the implementation. 57 | */ 58 | function _implementation() virtual internal view returns (address); 59 | 60 | /** 61 | * @dev Delegates execution to an implementation contract. 62 | * This is a low level function that doesn't return to its internal call site. 63 | * It will return to the external caller whatever the implementation returns. 64 | * @param implementation Address to delegate. 65 | */ 66 | function _delegate(address implementation) internal { 67 | assembly { 68 | // Copy msg.data. We take full control of memory in this inline assembly 69 | // block because it will not return to Solidity code. We overwrite the 70 | // Solidity scratch pad at memory position 0. 71 | calldatacopy(0, 0, calldatasize()) 72 | 73 | // Call the implementation. 74 | // out and outsize are 0 because we don't know the size yet. 75 | let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) 76 | 77 | // Copy the returned data. 78 | returndatacopy(0, 0, returndatasize()) 79 | 80 | switch result 81 | // delegatecall returns 0 on error. 82 | case 0 { revert(0, returndatasize()) } 83 | default { return(0, returndatasize()) } 84 | } 85 | } 86 | 87 | /** 88 | * @dev Function that is run as the first thing in the fallback function. 89 | * Can be redefined in derived contracts to add functionality. 90 | * Redefinitions must call super._willFallback(). 91 | */ 92 | function _willFallback() virtual internal { 93 | 94 | } 95 | 96 | /** 97 | * @dev fallback implementation. 98 | * Extracted to enable manual triggering. 99 | */ 100 | function _fallback() internal { 101 | if(OpenZeppelinUpgradesAddress.isContract(msg.sender) && msg.data.length == 0 && gasleft() <= 2300) // for receive ETH only from other contract 102 | return; 103 | _willFallback(); 104 | _delegate(_implementation()); 105 | } 106 | } 107 | 108 | 109 | /** 110 | * @title BaseUpgradeabilityProxy 111 | * @dev This contract implements a proxy that allows to change the 112 | * implementation address to which it will delegate. 113 | * Such a change is called an implementation upgrade. 114 | */ 115 | abstract contract BaseUpgradeabilityProxy is Proxy { 116 | /** 117 | * @dev Emitted when the implementation is upgraded. 118 | * @param implementation Address of the new implementation. 119 | */ 120 | event Upgraded(address indexed implementation); 121 | 122 | /** 123 | * @dev Storage slot with the address of the current implementation. 124 | * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is 125 | * validated in the constructor. 126 | */ 127 | bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; 128 | 129 | /** 130 | * @dev Returns the current implementation. 131 | * @return impl Address of the current implementation 132 | */ 133 | function _implementation() override internal view returns (address impl) { 134 | bytes32 slot = IMPLEMENTATION_SLOT; 135 | assembly { 136 | impl := sload(slot) 137 | } 138 | } 139 | 140 | /** 141 | * @dev Upgrades the proxy to a new implementation. 142 | * @param newImplementation Address of the new implementation. 143 | */ 144 | function _upgradeTo(address newImplementation) internal { 145 | _setImplementation(newImplementation); 146 | emit Upgraded(newImplementation); 147 | } 148 | 149 | /** 150 | * @dev Sets the implementation address of the proxy. 151 | * @param newImplementation Address of the new implementation. 152 | */ 153 | function _setImplementation(address newImplementation) internal { 154 | require(OpenZeppelinUpgradesAddress.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address"); 155 | 156 | bytes32 slot = IMPLEMENTATION_SLOT; 157 | 158 | assembly { 159 | sstore(slot, newImplementation) 160 | } 161 | } 162 | } 163 | 164 | 165 | /** 166 | * @title BaseAdminUpgradeabilityProxy 167 | * @dev This contract combines an upgradeability proxy with an authorization 168 | * mechanism for administrative tasks. 169 | * All external functions in this contract must be guarded by the 170 | * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity 171 | * feature proposal that would enable this to be done automatically. 172 | */ 173 | contract BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy { 174 | /** 175 | * @dev Emitted when the administration has been transferred. 176 | * @param previousAdmin Address of the previous admin. 177 | * @param newAdmin Address of the new admin. 178 | */ 179 | event AdminChanged(address previousAdmin, address newAdmin); 180 | event AdminPending(address admin, address pendingAdmin); 181 | 182 | 183 | /** 184 | * @dev Storage slot with the admin of the contract. 185 | * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is 186 | * validated in the constructor. 187 | */ 188 | 189 | bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; 190 | 191 | /** 192 | * @dev Storage slot with the pending admin of the contract. 193 | * This is the keccak-256 hash of "synassets.proxy.pendingadmin" subtracted by 1 194 | */ 195 | bytes32 internal constant PENDING_ADMIN_SLOT = 0x2e33589be650fc2441ee02dbd5d3b7b4666498c1b6698098f2920e8c31bd39f4; 196 | 197 | /** 198 | * @dev Modifier to check whether the `msg.sender` is the admin. 199 | * If it is, it will run the function. Otherwise, it will delegate the call 200 | * to the implementation. 201 | */ 202 | modifier ifAdmin() { 203 | if (msg.sender == _admin()) { 204 | _; 205 | } else { 206 | _fallback(); 207 | } 208 | } 209 | 210 | /** 211 | * @return The address of the proxy admin. 212 | */ 213 | function admin() external view returns (address) { 214 | return _admin(); 215 | } 216 | 217 | 218 | function pendingAdmin() external view returns (address) { 219 | return _pendingAdmin(); 220 | } 221 | 222 | /** 223 | * @return The address of the implementation. 224 | */ 225 | function implementation() external view returns (address) { 226 | return _implementation(); 227 | } 228 | 229 | function renounceAdmin() external ifAdmin { 230 | emit AdminChanged(_admin(), address(0)); 231 | _setAdmin(address(0)); 232 | _setPendingAdmin(address(0)); 233 | } 234 | 235 | /** 236 | * @dev Changes the admin of the proxy. 237 | * Only the current admin can call this function. 238 | * @param newAdmin Address to transfer proxy administration to. 239 | */ 240 | function changeAdmin(address newAdmin) external ifAdmin { 241 | require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address"); 242 | emit AdminPending(_admin(), newAdmin); 243 | _setPendingAdmin(newAdmin); 244 | } 245 | 246 | function accept() external { 247 | require(_pendingAdmin() == msg.sender, "Permission denied"); 248 | emit AdminChanged(_admin(), msg.sender); 249 | _setAdmin(msg.sender); 250 | _setPendingAdmin(address(0)); 251 | } 252 | 253 | /** 254 | * @dev Upgrade the backing implementation of the proxy. 255 | * Only the admin can call this function. 256 | * @param newImplementation Address of the new implementation. 257 | */ 258 | function upgradeTo(address newImplementation) external ifAdmin { 259 | _upgradeTo(newImplementation); 260 | } 261 | 262 | /** 263 | * @dev Upgrade the backing implementation of the proxy and call a function 264 | * on the new implementation. 265 | * This is useful to initialize the proxied contract. 266 | * @param newImplementation Address of the new implementation. 267 | * @param data Data to send as msg.data in the low level call. 268 | * It should include the signature and the parameters of the function to be called, as described in 269 | * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. 270 | */ 271 | function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin { 272 | _upgradeTo(newImplementation); 273 | (bool success,) = newImplementation.delegatecall(data); 274 | require(success); 275 | } 276 | 277 | /** 278 | * @return adm The admin slot. 279 | */ 280 | function _admin() internal view returns (address adm) { 281 | bytes32 slot = ADMIN_SLOT; 282 | assembly { 283 | adm := sload(slot) 284 | } 285 | } 286 | 287 | function _pendingAdmin() internal view returns (address pendingAdm) { 288 | bytes32 slot = PENDING_ADMIN_SLOT; 289 | assembly { 290 | pendingAdm := sload(slot) 291 | } 292 | } 293 | 294 | /** 295 | * @dev Sets the address of the proxy admin. 296 | * @param newAdmin Address of the new proxy admin. 297 | */ 298 | function _setAdmin(address newAdmin) internal { 299 | bytes32 slot = ADMIN_SLOT; 300 | 301 | assembly { 302 | sstore(slot, newAdmin) 303 | } 304 | } 305 | 306 | function _setPendingAdmin(address newPendingAdmin) internal { 307 | bytes32 slot = PENDING_ADMIN_SLOT; 308 | 309 | assembly { 310 | sstore(slot, newPendingAdmin) 311 | } 312 | } 313 | 314 | /** 315 | * @dev Only fall back when the sender is not the admin. 316 | */ 317 | function _willFallback() virtual override internal { 318 | require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin"); 319 | //super._willFallback(); 320 | } 321 | } 322 | 323 | interface IAdminUpgradeabilityProxyView { 324 | function admin() external view returns (address); 325 | function implementation() external view returns (address); 326 | } 327 | 328 | 329 | /**œ 330 | * @title UpgradeabilityProxy 331 | * @dev Extends BaseUpgradeabilityProxy with a constructor for initializing 332 | * implementation and init data. 333 | */ 334 | abstract contract UpgradeabilityProxy is BaseUpgradeabilityProxy { 335 | /** 336 | * @dev Contract constructor. 337 | * @param _logic Address of the initial implementation. 338 | * @param _data Data to send as msg.data to the implementation to initialize the proxied contract. 339 | * It should include the signature and the parameters of the function to be called, as described in 340 | * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. 341 | * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. 342 | */ 343 | constructor(address _logic, bytes memory _data) { 344 | assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)); 345 | _setImplementation(_logic); 346 | if(_data.length > 0) { 347 | (bool success,) = _logic.delegatecall(_data); 348 | require(success); 349 | } 350 | } 351 | 352 | //function _willFallback() virtual override internal { 353 | //super._willFallback(); 354 | //} 355 | } 356 | 357 | 358 | /** 359 | * @title AdminUpgradeabilityProxy 360 | * @dev Extends from BaseAdminUpgradeabilityProxy with a constructor for 361 | * initializing the implementation, admin, and init data. 362 | */ 363 | contract AdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, UpgradeabilityProxy { 364 | /** 365 | * Contract constructor. 366 | * @param _logic address of the initial implementation. 367 | * @param _admin Address of the proxy administrator. 368 | * @param _data Data to send as msg.data to the implementation to initialize the proxied contract. 369 | * It should include the signature and the parameters of the function to be called, as described in 370 | * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. 371 | * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. 372 | */ 373 | constructor(address _admin, address _logic, bytes memory _data) UpgradeabilityProxy(_logic, _data) { 374 | assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)); 375 | _setAdmin(_admin); 376 | } 377 | 378 | function _willFallback() override(Proxy, BaseAdminUpgradeabilityProxy) internal { 379 | super._willFallback(); 380 | } 381 | } 382 | -------------------------------------------------------------------------------- /contracts/ConsensusPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.7.5; 3 | 4 | library LowGasSafeMath { 5 | /// @notice Returns x + y, reverts if sum overflows uint256 6 | /// @param x The augend 7 | /// @param y The addend 8 | /// @return z The sum of x and y 9 | function add(uint256 x, uint256 y) internal pure returns (uint256 z) { 10 | require((z = x + y) >= x); 11 | } 12 | 13 | function add32(uint32 x, uint32 y) internal pure returns (uint32 z) { 14 | require((z = x + y) >= x); 15 | } 16 | 17 | /// @notice Returns x - y, reverts if underflows 18 | /// @param x The minuend 19 | /// @param y The subtrahend 20 | /// @return z The difference of x and y 21 | function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { 22 | require((z = x - y) <= x); 23 | } 24 | 25 | function sub32(uint32 x, uint32 y) internal pure returns (uint32 z) { 26 | require((z = x - y) <= x); 27 | } 28 | 29 | /// @notice Returns x * y, reverts if overflows 30 | /// @param x The multiplicand 31 | /// @param y The multiplier 32 | /// @return z The product of x and y 33 | function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { 34 | require(x == 0 || (z = x * y) / x == y); 35 | } 36 | 37 | function mul32(uint32 x, uint32 y) internal pure returns (uint32 z) { 38 | require(x == 0 || (z = x * y) / x == y); 39 | } 40 | 41 | /// @notice Returns x + y, reverts if overflows or underflows 42 | /// @param x The augend 43 | /// @param y The addend 44 | /// @return z The sum of x and y 45 | function add(int256 x, int256 y) internal pure returns (int256 z) { 46 | require((z = x + y) >= x == (y >= 0)); 47 | } 48 | 49 | /// @notice Returns x - y, reverts if overflows or underflows 50 | /// @param x The minuend 51 | /// @param y The subtrahend 52 | /// @return z The difference of x and y 53 | function sub(int256 x, int256 y) internal pure returns (int256 z) { 54 | require((z = x - y) <= x == (y >= 0)); 55 | } 56 | 57 | function div(uint256 x, uint256 y) internal pure returns(uint256 z){ 58 | require(y > 0); 59 | z=x/y; 60 | } 61 | } 62 | 63 | library Address { 64 | 65 | function isContract(address account) internal view returns (bool) { 66 | // This method relies in extcodesize, which returns 0 for contracts in 67 | // construction, since the code is only stored at the end of the 68 | // constructor execution. 69 | 70 | uint256 size; 71 | // solhint-disable-next-line no-inline-assembly 72 | assembly { size := extcodesize(account) } 73 | return size > 0; 74 | } 75 | 76 | function functionCall( 77 | address target, 78 | bytes memory data, 79 | string memory errorMessage 80 | ) internal returns (bytes memory) { 81 | return _functionCallWithValue(target, data, 0, errorMessage); 82 | } 83 | 84 | function _functionCallWithValue( 85 | address target, 86 | bytes memory data, 87 | uint256 weiValue, 88 | string memory errorMessage 89 | ) private returns (bytes memory) { 90 | require(isContract(target), "Address: call to non-contract"); 91 | 92 | // solhint-disable-next-line avoid-low-level-calls 93 | (bool success, bytes memory returndata) = target.call{ value: weiValue }(data); 94 | if (success) { 95 | return returndata; 96 | } else { 97 | if (returndata.length > 0) { 98 | // solhint-disable-next-line no-inline-assembly 99 | assembly { 100 | let returndata_size := mload(returndata) 101 | revert(add(32, returndata), returndata_size) 102 | } 103 | } else { 104 | revert(errorMessage); 105 | } 106 | } 107 | } 108 | 109 | function _verifyCallResult( 110 | bool success, 111 | bytes memory returndata, 112 | string memory errorMessage 113 | ) private pure returns(bytes memory) { 114 | if (success) { 115 | return returndata; 116 | } else { 117 | if (returndata.length > 0) { 118 | // solhint-disable-next-line no-inline-assembly 119 | assembly { 120 | let returndata_size := mload(returndata) 121 | revert(add(32, returndata), returndata_size) 122 | } 123 | } else { 124 | revert(errorMessage); 125 | } 126 | } 127 | } 128 | } 129 | 130 | interface IERC20 { 131 | function decimals() external view returns (uint8); 132 | 133 | function balanceOf(address account) external view returns (uint256); 134 | 135 | function transfer(address recipient, uint256 amount) external returns (bool); 136 | 137 | function approve(address spender, uint256 amount) external returns (bool); 138 | 139 | function totalSupply() external view returns (uint256); 140 | 141 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 142 | 143 | function burn(uint256 amount) external; 144 | 145 | event Transfer(address indexed from, address indexed to, uint256 value); 146 | 147 | event Approval(address indexed owner, address indexed spender, uint256 value); 148 | } 149 | 150 | library SafeERC20 { 151 | using LowGasSafeMath for uint256; 152 | using Address for address; 153 | 154 | function safeTransfer(IERC20 token, address to, uint256 value) internal { 155 | _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); 156 | } 157 | 158 | function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { 159 | _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); 160 | } 161 | 162 | function _callOptionalReturn(IERC20 token, bytes memory data) private { 163 | bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); 164 | if (returndata.length > 0) { // Return data is optional 165 | // solhint-disable-next-line max-line-length 166 | require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); 167 | } 168 | } 169 | } 170 | 171 | interface IDistributor { 172 | function distribute() external returns ( bool ); 173 | } 174 | 175 | // 7.97% 176 | contract ConsensusPool { 177 | 178 | using LowGasSafeMath for uint256; 179 | using SafeERC20 for IERC20; 180 | 181 | struct UserInfo { 182 | uint256 power; 183 | uint256 number; 184 | uint256 reward; 185 | uint256 totalReward; 186 | uint256 rewardCounter; 187 | uint256 claimCounter; 188 | uint256 burnAmount; 189 | uint256 totalBurnAmount; 190 | uint256 inviteeCounter; 191 | address inviter; 192 | } 193 | 194 | struct InviteeInfo { 195 | uint256 power; 196 | uint256 claimCounterSnapshot; 197 | } 198 | 199 | struct Epoch { 200 | uint256 length; 201 | uint256 number; 202 | uint256 endBlock; 203 | uint256 distribute; 204 | } 205 | 206 | struct EpochInfo { 207 | uint256 rewardPerPower; 208 | uint256 rewardPerPowerStoredSnapshot; 209 | } 210 | 211 | address public SYNASSETS; 212 | address public sSYNASSETS; 213 | 214 | uint256 public totalPower; 215 | uint256 public rewardPerPowerStored; 216 | uint256 public totalRewardReserves; 217 | mapping(address => UserInfo) public userInfos; 218 | mapping(address => mapping(address => InviteeInfo)) public inviteeInfos; 219 | 220 | mapping(uint256 => EpochInfo) public epochInfos; 221 | 222 | Epoch public epoch; 223 | address public distributor; 224 | address public stakingContract; 225 | 226 | uint256 public constant REWARD_LIMIT = 9; 227 | uint256 public constant RATIO_DECAY = 797; // in(1/10000) 228 | uint256 public constant RATIO_DECAY_T10 = 5642; 229 | 230 | event Invited(address indexed inviter, address user); 231 | event RewardAdded(uint256 reward); 232 | event RewardBurn(address indexed account, uint256 reward); 233 | event Staked(address indexed staker, address indexed inviter, uint256 amount); 234 | event Unstaked(address indexed unstaker, address indexed inviter, uint256 amount); 235 | event RewardPaid(address indexed user, uint256 reward); 236 | 237 | function initialize ( 238 | address _SYNASSETS, 239 | address _sSYNASSETS, 240 | uint256 _epochLength, 241 | uint256 _firstEpochNumber, 242 | uint256 _firstEpochBlock, 243 | address _stakingContract, 244 | address _distributor 245 | ) external { 246 | require( SYNASSETS == address(0), 'AI' ); 247 | 248 | require( _SYNASSETS != address(0) ); 249 | SYNASSETS = _SYNASSETS; 250 | 251 | require( _sSYNASSETS != address(0) ); 252 | sSYNASSETS = _sSYNASSETS; 253 | 254 | epoch = Epoch({ 255 | length: _epochLength, 256 | number: _firstEpochNumber, 257 | endBlock: _firstEpochBlock, 258 | distribute: 0 259 | }); 260 | 261 | stakingContract = _stakingContract; 262 | distributor = _distributor; 263 | } 264 | 265 | modifier onlyStakingContract() { 266 | require(msg.sender == stakingContract, 'OSC'); 267 | _; 268 | } 269 | 270 | /* ====== PUBLIC FUNCTIONS ====== */ 271 | 272 | function stake(address _staker, address _inviter, uint256 _amount) external onlyStakingContract() { 273 | if (userInfos[_staker].inviter == address(0)) { 274 | userInfos[_staker].inviter = _inviter; 275 | userInfos[_inviter].inviteeCounter ++; 276 | emit Invited(_inviter, _staker); 277 | } 278 | _inviter = userInfos[_staker].inviter; 279 | require(_inviter != address(0), 'IA'); 280 | 281 | _notifyRewardAmount(); 282 | _updateReward(_inviter); 283 | 284 | totalPower = totalPower.add(_amount); 285 | userInfos[_inviter].power = userInfos[_inviter].power.add(_amount); 286 | 287 | uint256 _powerBefore = _calcPower(_inviter, _staker); 288 | inviteeInfos[_inviter][_staker].power = _powerBefore.add(_amount); 289 | inviteeInfos[_inviter][_staker].claimCounterSnapshot = userInfos[_inviter].claimCounter; 290 | 291 | emit Staked(_staker, _inviter, _amount); 292 | } 293 | 294 | function unstake(address _unstaker, uint256 _amount) external onlyStakingContract() { 295 | address _inviter = userInfos[_unstaker].inviter; 296 | 297 | _updateReward(_inviter); 298 | 299 | uint256 _powerBefore = _calcPower(_inviter, _unstaker); 300 | uint256 _powerAfter = _powerBefore.sub(_powerBefore.mul(_amount).div(_amount.add(IERC20(sSYNASSETS).balanceOf(_unstaker)))); 301 | 302 | totalPower = totalPower.add(_powerAfter).sub(_powerBefore); 303 | userInfos[_inviter].power = userInfos[_inviter].power.add(_powerAfter).sub(_powerBefore); 304 | 305 | inviteeInfos[_inviter][_unstaker].power = _powerAfter; 306 | inviteeInfos[_inviter][_unstaker].claimCounterSnapshot = userInfos[_inviter].claimCounter; 307 | 308 | emit Unstaked(_unstaker, _inviter, _amount); 309 | } 310 | 311 | function claimReward() external { 312 | _updateReward(msg.sender); 313 | 314 | uint256 _reward = userInfos[msg.sender].reward; 315 | if (_reward > 0) { 316 | userInfos[msg.sender].reward = 0; 317 | userInfos[msg.sender].claimCounter = userInfos[msg.sender].claimCounter.add(userInfos[msg.sender].rewardCounter); 318 | userInfos[msg.sender].rewardCounter = 0; 319 | IERC20(SYNASSETS).safeTransfer(msg.sender, _reward); 320 | 321 | uint256 _burnAmount = userInfos[msg.sender].burnAmount; 322 | if (_burnAmount > 0) { 323 | IERC20(SYNASSETS).burn(_burnAmount); 324 | emit RewardBurn(msg.sender, _burnAmount); 325 | 326 | userInfos[msg.sender].burnAmount = 0; 327 | } 328 | 329 | totalRewardReserves = totalRewardReserves.sub(_reward.add(_burnAmount)); 330 | 331 | emit RewardPaid(msg.sender, _reward); 332 | } 333 | } 334 | 335 | /* ====== INTERNAL FUNCTIONS ====== */ 336 | 337 | function _updateReward(address _account) internal { 338 | UserInfo memory _userInfo = userInfos[_account]; 339 | if (_userInfo.power > 0) { 340 | (uint256 _reward, uint256 _number, uint256 _power, uint256 _rewardCounter) = _calcReward(_userInfo); 341 | 342 | if (_rewardCounter > 0) { 343 | userInfos[_account].power = _power; 344 | totalPower = totalPower.add(_power).sub(_userInfo.power); 345 | 346 | userInfos[_account].reward = _userInfo.reward.add(_reward); 347 | userInfos[_account].totalReward = _userInfo.totalReward.add(_reward); 348 | userInfos[_account].rewardCounter = _userInfo.rewardCounter.add(_rewardCounter); 349 | } 350 | 351 | if (_number < epoch.number) { 352 | uint256 burnAmountPerPower = rewardPerPowerStored.sub(epochInfos[_number].rewardPerPowerStoredSnapshot); 353 | if (burnAmountPerPower > 0) { 354 | uint256 burnAmount = _userInfo.power.mul(burnAmountPerPower).div(1 ether); 355 | 356 | userInfos[_account].burnAmount = _userInfo.burnAmount.add(burnAmount); 357 | userInfos[_account].totalBurnAmount = _userInfo.totalBurnAmount.add(burnAmount); 358 | } 359 | } 360 | } 361 | 362 | userInfos[_account].number = epoch.number; 363 | } 364 | 365 | function _notifyRewardAmount() internal { 366 | if (epoch.endBlock <= block.number) { 367 | uint256 distribute = epoch.distribute; 368 | if (distribute > 0) { 369 | emit RewardAdded(distribute); 370 | 371 | if (totalPower == 0) { 372 | IERC20(SYNASSETS).burn(distribute); 373 | emit RewardBurn(address(0), distribute); 374 | 375 | totalRewardReserves = totalRewardReserves.sub(distribute); 376 | } else { 377 | uint256 _rewardPerPower = distribute.mul(1 ether).div(totalPower); 378 | rewardPerPowerStored = rewardPerPowerStored.add(_rewardPerPower); 379 | uint256 number = epoch.number; 380 | epochInfos[number].rewardPerPower = _rewardPerPower; 381 | epochInfos[number].rewardPerPowerStoredSnapshot = rewardPerPowerStored; 382 | } 383 | } 384 | 385 | epoch.endBlock = epoch.endBlock.add(epoch.length); 386 | epoch.number++; 387 | 388 | if (distributor != address(0)) { 389 | IDistributor(distributor).distribute(); 390 | } 391 | 392 | uint256 balance = IERC20(SYNASSETS).balanceOf(address(this)); 393 | if (balance <= totalRewardReserves) 394 | epoch.distribute = 0; 395 | else 396 | epoch.distribute = balance.sub(totalRewardReserves); 397 | 398 | totalRewardReserves = balance; 399 | } 400 | } 401 | 402 | /* ====== VIEW FUNCTIONS ====== */ 403 | 404 | function getInfo(address _account) public view returns (uint256 claimableAmount, uint256 totalReward, uint256 power, uint256 inviteNum, uint256 burnAmount) { 405 | UserInfo memory _userInfo = userInfos[_account]; 406 | (uint256 _reward, , uint256 _power,) = _calcReward(_userInfo); 407 | 408 | return (_reward.add(_userInfo.reward), _reward.add(_userInfo.totalReward), _power, _userInfo.inviteeCounter, _userInfo.totalBurnAmount); 409 | } 410 | 411 | function _calcPower(address _account, address _invitee) internal view returns (uint256) { 412 | uint256 _claimCounter = userInfos[_account].claimCounter; 413 | InviteeInfo memory _info = inviteeInfos[_account][_invitee]; 414 | 415 | if (_claimCounter <= _info.claimCounterSnapshot) return _info.power; 416 | 417 | uint256 _t = _claimCounter - _info.claimCounterSnapshot; 418 | if (_t >= 100) return 0; 419 | 420 | uint256 _powerDecay = _info.power; 421 | uint256 _t10 = _t / 10; 422 | if (_t10 > 0) _powerDecay = _powerDecay.mul((10000 - RATIO_DECAY_T10) ** _t10).div(10000 ** _t10); 423 | uint256 _t1 = _t % 10; 424 | if (_t1 > 0) _powerDecay = _powerDecay.mul((10000 - RATIO_DECAY) ** _t1).div(10000 ** _t1); 425 | 426 | return _powerDecay; 427 | } 428 | 429 | function _calcReward(UserInfo memory _userInfo) internal view returns (uint256 reward_, uint256 number_, uint256 power_, uint256 rewardCounter_) { 430 | uint256 _number = epoch.number; 431 | 432 | reward_ = 0; 433 | number_ = _userInfo.number; 434 | power_ = _userInfo.power; 435 | rewardCounter_ = 0; 436 | for (; number_ < _number && _userInfo.rewardCounter.add(rewardCounter_) < REWARD_LIMIT; number_ = number_.add(1)) { 437 | reward_ = reward_.add(epochInfos[number_].rewardPerPower.mul(power_).div(1 ether)); 438 | power_ = power_.mul((10000 - RATIO_DECAY)).div(10000); 439 | rewardCounter_ ++; 440 | } 441 | 442 | number_ = number_.sub(1); 443 | } 444 | 445 | } 446 | -------------------------------------------------------------------------------- /contracts/SAT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.7.5; 3 | 4 | /** 5 | * @title Initializable 6 | * 7 | * @dev Helper contract to support initializer functions. To use it, replace 8 | * the constructor with a function that has the `initializer` modifier. 9 | * WARNING: Unlike constructors, initializer functions must be manually 10 | * invoked. This applies both to deploying an Initializable contract, as well 11 | * as extending an Initializable contract via inheritance. 12 | * WARNING: When used with inheritance, manual care must be taken to not invoke 13 | * a parent initializer twice, or ensure that all initializers are idempotent, 14 | * because this is not dealt with automatically as with constructors. 15 | */ 16 | contract Initializable { 17 | 18 | /** 19 | * @dev Indicates that the contract has been initialized. 20 | */ 21 | bool private initialized; 22 | 23 | /** 24 | * @dev Indicates that the contract is in the process of being initialized. 25 | */ 26 | bool private initializing; 27 | 28 | /** 29 | * @dev Modifier to use in the initializer function of a contract. 30 | */ 31 | modifier initializer() { 32 | require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized"); 33 | 34 | bool isTopLevelCall = !initializing; 35 | if (isTopLevelCall) { 36 | initializing = true; 37 | initialized = true; 38 | } 39 | 40 | _; 41 | 42 | if (isTopLevelCall) { 43 | initializing = false; 44 | } 45 | } 46 | 47 | /// @dev Returns true if and only if the function is running in the constructor 48 | function isConstructor() private view returns (bool) { 49 | // extcodesize checks the size of the code stored in an address, and 50 | // address returns the current address. Since the code is still not 51 | // deployed when running a constructor, any checks on its code size will 52 | // yield zero, making it an effective way to detect if a contract is 53 | // under construction or not. 54 | address self = address(this); 55 | uint256 cs; 56 | assembly { cs := extcodesize(self) } 57 | return cs == 0; 58 | } 59 | } 60 | 61 | interface IERC20 { 62 | /** 63 | * @dev Returns the amount of tokens in existence. 64 | */ 65 | function totalSupply() external view returns (uint256); 66 | 67 | /** 68 | * @dev Returns the amount of tokens owned by `account`. 69 | */ 70 | function balanceOf(address account) external view returns (uint256); 71 | 72 | /** 73 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 74 | * 75 | * Returns a boolean value indicating whether the operation succeeded. 76 | * 77 | * Emits a {Transfer} event. 78 | */ 79 | function transfer(address recipient, uint256 amount) external returns (bool); 80 | 81 | /** 82 | * @dev Returns the remaining number of tokens that `spender` will be 83 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 84 | * zero by default. 85 | * 86 | * This value changes when {approve} or {transferFrom} are called. 87 | */ 88 | function allowance(address owner, address spender) external view returns (uint256); 89 | 90 | /** 91 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 92 | * 93 | * Returns a boolean value indicating whether the operation succeeded. 94 | * 95 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 96 | * that someone may use both the old and the new allowance by unfortunate 97 | * transaction ordering. One possible solution to mitigate this race 98 | * condition is to first reduce the spender's allowance to 0 and set the 99 | * desired value afterwards: 100 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 101 | * 102 | * Emits an {Approval} event. 103 | */ 104 | function approve(address spender, uint256 amount) external returns (bool); 105 | 106 | /** 107 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 108 | * allowance mechanism. `amount` is then deducted from the caller's 109 | * allowance. 110 | * 111 | * Returns a boolean value indicating whether the operation succeeded. 112 | * 113 | * Emits a {Transfer} event. 114 | */ 115 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 116 | 117 | /** 118 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 119 | * another (`to`). 120 | * 121 | * Note that `value` may be zero. 122 | */ 123 | event Transfer(address indexed from, address indexed to, uint256 value); 124 | 125 | /** 126 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 127 | * a call to {approve}. `value` is the new allowance. 128 | */ 129 | event Approval(address indexed owner, address indexed spender, uint256 value); 130 | } 131 | 132 | library LowGasSafeMath { 133 | /// @notice Returns x + y, reverts if sum overflows uint256 134 | /// @param x The augend 135 | /// @param y The addend 136 | /// @return z The sum of x and y 137 | function add(uint256 x, uint256 y) internal pure returns (uint256 z) { 138 | require((z = x + y) >= x); 139 | } 140 | 141 | /// @notice Returns x - y, reverts if underflows 142 | /// @param x The minuend 143 | /// @param y The subtrahend 144 | /// @return z The difference of x and y 145 | function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { 146 | require((z = x - y) <= x); 147 | } 148 | 149 | /// @notice Returns x * y, reverts if overflows 150 | /// @param x The multiplicand 151 | /// @param y The multiplier 152 | /// @return z The product of x and y 153 | function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { 154 | require(x == 0 || (z = x * y) / x == y); 155 | } 156 | 157 | /// @notice Returns x + y, reverts if overflows or underflows 158 | /// @param x The augend 159 | /// @param y The addend 160 | /// @return z The sum of x and y 161 | function add(int256 x, int256 y) internal pure returns (int256 z) { 162 | require((z = x + y) >= x == (y >= 0)); 163 | } 164 | 165 | /// @notice Returns x - y, reverts if overflows or underflows 166 | /// @param x The minuend 167 | /// @param y The subtrahend 168 | /// @return z The difference of x and y 169 | function sub(int256 x, int256 y) internal pure returns (int256 z) { 170 | require((z = x - y) <= x == (y >= 0)); 171 | } 172 | 173 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 174 | return div(a, b, "SafeMath: division by zero"); 175 | } 176 | 177 | /** 178 | * @dev Returns the integer division of two unsigned integers. Reverts with custom message on 179 | * division by zero. The result is rounded towards zero. 180 | * 181 | * Counterpart to Solidity's `/` operator. Note: this function uses a 182 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 183 | * uses an invalid opcode to revert (consuming all remaining gas). 184 | * 185 | * Requirements: 186 | * 187 | * - The divisor cannot be zero. 188 | */ 189 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 190 | require(b > 0, errorMessage); 191 | uint256 c = a / b; 192 | assert(a == b * c + a % b); // There is no case in which this doesn't hold 193 | 194 | return c; 195 | } 196 | } 197 | 198 | abstract contract ERC20 is IERC20, Initializable { 199 | 200 | using LowGasSafeMath for uint256; 201 | 202 | // Present in ERC777 203 | mapping (address => uint256) internal _balances; 204 | 205 | // Present in ERC777 206 | mapping (address => mapping (address => uint256)) internal _allowances; 207 | 208 | // Present in ERC777 209 | uint256 internal _totalSupply; 210 | 211 | // Present in ERC777 212 | string internal _name; 213 | 214 | // Present in ERC777 215 | string internal _symbol; 216 | 217 | // Present in ERC777 218 | uint8 internal _decimals; 219 | 220 | function __ERC20_init_unchained(string memory name_, string memory symbol_, uint8 decimals_) internal initializer { 221 | _name = name_; 222 | _symbol = symbol_; 223 | _decimals = decimals_; 224 | } 225 | 226 | function name() public view returns (string memory) { 227 | return _name; 228 | } 229 | 230 | function symbol() public view returns (string memory) { 231 | return _symbol; 232 | } 233 | 234 | function decimals() public view returns (uint8) { 235 | return _decimals; 236 | } 237 | 238 | function totalSupply() public view override returns (uint256) { 239 | return _totalSupply; 240 | } 241 | 242 | function balanceOf(address account) public view virtual override returns (uint256) { 243 | return _balances[account]; 244 | } 245 | 246 | function transfer(address recipient, uint256 amount) public virtual override returns (bool) { 247 | _transfer(msg.sender, recipient, amount); 248 | return true; 249 | } 250 | 251 | function allowance(address owner, address spender) public view virtual override returns (uint256) { 252 | return _allowances[owner][spender]; 253 | } 254 | 255 | function approve(address spender, uint256 amount) public virtual override returns (bool) { 256 | _approve(msg.sender, spender, amount); 257 | return true; 258 | } 259 | 260 | function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { 261 | _transfer(sender, recipient, amount); 262 | _approve(sender, msg.sender, _allowances[sender][msg.sender] 263 | .sub(amount)); 264 | return true; 265 | } 266 | 267 | function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { 268 | _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue)); 269 | return true; 270 | } 271 | 272 | function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { 273 | _approve(msg.sender, spender, _allowances[msg.sender][spender] 274 | .sub(subtractedValue)); 275 | return true; 276 | } 277 | 278 | function _transfer(address sender, address recipient, uint256 amount) internal virtual { 279 | require(sender != address(0), "ERC20: transfer from the zero address"); 280 | require(recipient != address(0), "ERC20: transfer to the zero address"); 281 | 282 | _beforeTokenTransfer(sender, recipient, amount); 283 | 284 | _balances[sender] = _balances[sender].sub(amount); 285 | _balances[recipient] = _balances[recipient].add(amount); 286 | emit Transfer(sender, recipient, amount); 287 | } 288 | 289 | 290 | function _sub_balances(address account_, uint256 amount_) internal { 291 | require(account_ != address(0), "ERC20: mint to the zero address"); 292 | _balances[account_] = _balances[account_].sub(amount_); 293 | } 294 | function _add_balances(address account_, uint256 amount_) internal { 295 | require(account_ != address(0), "ERC20: mint to the zero address"); 296 | _balances[account_] = _balances[account_].add(amount_); 297 | } 298 | 299 | function _mint(address account_, uint256 amount_) internal virtual { 300 | require(account_ != address(0), "ERC20: mint to the zero address"); 301 | _beforeTokenTransfer(address( this ), account_, amount_); 302 | _totalSupply = _totalSupply.add(amount_); 303 | _balances[account_] = _balances[account_].add(amount_); 304 | emit Transfer(address(0), account_, amount_); 305 | } 306 | 307 | function _burn(address account, uint256 amount) internal virtual { 308 | require(account != address(0), "ERC20: burn from the zero address"); 309 | 310 | _beforeTokenTransfer(account, address(0), amount); 311 | 312 | _balances[account] = _balances[account].sub(amount); 313 | _totalSupply = _totalSupply.sub(amount); 314 | emit Transfer(account, address(0), amount); 315 | } 316 | 317 | function _approve(address owner, address spender, uint256 amount) internal virtual { 318 | require(owner != address(0), "ERC20: approve from the zero address"); 319 | require(spender != address(0), "ERC20: approve to the zero address"); 320 | 321 | _allowances[owner][spender] = amount; 322 | emit Approval(owner, spender, amount); 323 | } 324 | 325 | function _beforeTokenTransfer( address from_, address to_, uint256 amount_ ) internal virtual { } 326 | } 327 | 328 | library Counters { 329 | using LowGasSafeMath for uint256; 330 | 331 | struct Counter { 332 | uint256 _value; // default: 0 333 | } 334 | 335 | function current(Counter storage counter) internal view returns (uint256) { 336 | return counter._value; 337 | } 338 | 339 | function increment(Counter storage counter) internal { 340 | counter._value += 1; 341 | } 342 | 343 | function decrement(Counter storage counter) internal { 344 | counter._value = counter._value.sub(1); 345 | } 346 | } 347 | 348 | interface IERC2612Permit { 349 | 350 | function permit( 351 | address owner, 352 | address spender, 353 | uint256 amount, 354 | uint256 deadline, 355 | uint8 v, 356 | bytes32 r, 357 | bytes32 s 358 | ) external; 359 | 360 | function nonces(address owner) external view returns (uint256); 361 | } 362 | 363 | abstract contract ERC20Permit is ERC20, IERC2612Permit { 364 | using Counters for Counters.Counter; 365 | 366 | mapping(address => Counters.Counter) private _nonces; 367 | 368 | // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); 369 | bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; 370 | 371 | bytes32 public DOMAIN_SEPARATOR; 372 | 373 | constructor() { 374 | uint256 chainID; 375 | assembly { 376 | chainID := chainid() 377 | } 378 | 379 | DOMAIN_SEPARATOR = keccak256( 380 | abi.encode( 381 | keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), 382 | keccak256(bytes(name())), 383 | keccak256(bytes("1")), // Version 384 | chainID, 385 | address(this) 386 | ) 387 | ); 388 | } 389 | 390 | function permit( 391 | address owner, 392 | address spender, 393 | uint256 amount, 394 | uint256 deadline, 395 | uint8 v, 396 | bytes32 r, 397 | bytes32 s 398 | ) public virtual override { 399 | require(block.timestamp <= deadline, "Permit: expired deadline"); 400 | 401 | bytes32 hashStruct = 402 | keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, amount, _nonces[owner].current(), deadline)); 403 | 404 | bytes32 _hash = keccak256(abi.encodePacked(uint16(0x1901), DOMAIN_SEPARATOR, hashStruct)); 405 | 406 | address signer = ecrecover(_hash, v, r, s); 407 | require(signer != address(0) && signer == owner, "ERC20Permit: Invalid signature"); 408 | 409 | _nonces[owner].increment(); 410 | _approve(owner, spender, amount); 411 | } 412 | 413 | function nonces(address owner) public view override returns (uint256) { 414 | return _nonces[owner].current(); 415 | } 416 | } 417 | 418 | /** 419 | * @dev Contract module which allows children to implement an emergency stop 420 | * mechanism that can be triggered by an authorized account. 421 | * 422 | * This module is used through inheritance. It will make available the 423 | * modifiers `whenNotPaused` and `whenPaused`, which can be applied to 424 | * the functions of your contract. Note that they will not be pausable by 425 | * simply including this module, only once the modifiers are put in place. 426 | */ 427 | abstract contract Pausable { 428 | /** 429 | * @dev Emitted when the pause is triggered by `account`. 430 | */ 431 | event Paused(address account); 432 | 433 | /** 434 | * @dev Emitted when the pause is lifted by `account`. 435 | */ 436 | event Unpaused(address account); 437 | 438 | bool private _paused; 439 | 440 | /** 441 | * @dev Initializes the contract in unpaused state. 442 | */ 443 | constructor() { 444 | _paused = false; 445 | } 446 | 447 | /** 448 | * @dev Returns true if the contract is paused, and false otherwise. 449 | */ 450 | function paused() public view virtual returns (bool) { 451 | return _paused; 452 | } 453 | 454 | /** 455 | * @dev Modifier to make a function callable only when the contract is not paused. 456 | * 457 | * Requirements: 458 | * 459 | * - The contract must not be paused. 460 | */ 461 | modifier whenNotPaused() { 462 | require(!paused(), "Pausable: paused"); 463 | _; 464 | } 465 | 466 | /** 467 | * @dev Modifier to make a function callable only when the contract is paused. 468 | * 469 | * Requirements: 470 | * 471 | * - The contract must be paused. 472 | */ 473 | modifier whenPaused() { 474 | require(paused(), "Pausable: not paused"); 475 | _; 476 | } 477 | 478 | /** 479 | * @dev Triggers stopped state. 480 | * 481 | * Requirements: 482 | * 483 | * - The contract must not be paused. 484 | */ 485 | function _pause() internal virtual whenNotPaused { 486 | _paused = true; 487 | emit Paused(msg.sender); 488 | } 489 | 490 | /** 491 | * @dev Returns to normal state. 492 | * 493 | * Requirements: 494 | * 495 | * - The contract must be paused. 496 | */ 497 | function _unpause() internal virtual whenPaused { 498 | _paused = false; 499 | emit Unpaused(msg.sender); 500 | } 501 | } 502 | 503 | interface IOwnable { 504 | function owner() external view returns (address); 505 | 506 | function renounceOwnership() external; 507 | 508 | function transferOwnership( address newOwner_ ) external; 509 | } 510 | 511 | contract Ownable is IOwnable, Initializable { 512 | 513 | address internal _owner; 514 | address internal _pendingOwner; 515 | 516 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 517 | event OwnershipTransferring(address indexed owner, address indexed pendingOwner); 518 | 519 | function __Ownable_init_unchain() internal initializer { 520 | require(_owner == address(0)); 521 | _owner = msg.sender; 522 | emit OwnershipTransferred( address(0), _owner ); 523 | } 524 | 525 | function owner() public view override returns (address) { 526 | return _owner; 527 | } 528 | 529 | modifier onlyOwner() { 530 | require( _owner == msg.sender, "Ownable: caller is not the owner" ); 531 | _; 532 | } 533 | 534 | function renounceOwnership() public virtual override onlyOwner() { 535 | emit OwnershipTransferred( _owner, address(0) ); 536 | _owner = address(0); 537 | } 538 | 539 | function transferOwnership( address newOwner_ ) public virtual override onlyOwner() { 540 | require( newOwner_ != address(0), "Ownable: new owner is the zero address"); 541 | emit OwnershipTransferring( _owner, newOwner_ ); 542 | _pendingOwner = newOwner_; 543 | } 544 | 545 | function acceptOwnership() external { 546 | require(_pendingOwner == msg.sender, "Permission denied"); 547 | emit OwnershipTransferred( _owner, msg.sender ); 548 | _owner = msg.sender; 549 | } 550 | } 551 | 552 | contract VaultOwned is Ownable { 553 | 554 | function __VaultOwned_init_unchain() internal initializer { 555 | __Ownable_init_unchain(); 556 | } 557 | 558 | address internal _vault; 559 | 560 | event VaultTransferred(address indexed newVault); 561 | 562 | function setVault( address vault_ ) external onlyOwner() { 563 | require(vault_ != address(0), "IA0"); 564 | _vault = vault_; 565 | emit VaultTransferred( _vault ); 566 | } 567 | 568 | function vault() public view returns (address) { 569 | return _vault; 570 | } 571 | 572 | modifier onlyVault() { 573 | require( _vault == msg.sender, "VaultOwned: caller is not the Vault" ); 574 | _; 575 | } 576 | 577 | } 578 | 579 | interface ISATTimelock { 580 | function increaseReward() external; 581 | } 582 | struct LOCK_OBJ { 583 | uint release_time; 584 | uint256 balance; 585 | } 586 | struct LOCK_INFO { 587 | LOCK_OBJ[] lock_array; 588 | } 589 | 590 | contract SATERC20Token is ERC20Permit, VaultOwned, Pausable { 591 | using LowGasSafeMath for uint256; 592 | uint256 public constant CAP = 10**8 * 1 ether; 593 | uint256 public constant RATIO_FEE = 0.1 ether; 594 | 595 | address public feeAddress; 596 | mapping(address => bool) public isTransferWhitelist; 597 | mapping(address => LOCK_INFO) internal lock; 598 | 599 | function __SATERC20Token_initialize() external initializer { 600 | __ERC20_init_unchained("Synassets Token", "SAT", 18); 601 | __VaultOwned_init_unchain(); 602 | } 603 | 604 | function internal_mint(address account_, uint256 amount_) internal{ 605 | require(totalSupply().add(amount_) <= CAP, 'CE'); 606 | _mint(account_, amount_); 607 | if (feeAddress != address(0)) { 608 | uint256 fee_ = amount_.mul(RATIO_FEE).div(1 ether); 609 | require(totalSupply().add(fee_) <= CAP, 'CE'); 610 | 611 | _mint(feeAddress, fee_); 612 | ISATTimelock(feeAddress).increaseReward(); 613 | } 614 | } 615 | 616 | function mint(address account_, uint256 amount_) external onlyVault() { 617 | internal_mint(account_, amount_); 618 | } 619 | 620 | 621 | function _freeze(address account_,uint256 amount_,uint256 release_time)internal { 622 | // require(release_time < block.timestamp +365*24*3600, 'release time is too long'); 623 | // require(release_time > block.timestamp , 'release time is before now'); 624 | _sub_balances(account_,amount_); 625 | lock[account_].lock_array.push(LOCK_OBJ(release_time,amount_)); 626 | } 627 | 628 | function lock_mint(address account_, uint256 amount_,uint256 release_time) external onlyOwner() { 629 | internal_mint(account_, amount_); 630 | _freeze(account_,amount_,release_time); 631 | } 632 | function lock_mint_batch(address[] calldata account_, uint256 amount_,uint256 release_time) external onlyOwner() { 633 | for (uint256 index = 0; index < account_.length; index ++) { 634 | internal_mint(account_[index], amount_); 635 | _freeze(account_[index],amount_,release_time); 636 | } 637 | 638 | } 639 | 640 | function get_LOCK_INFO(address account_) public view returns (uint256,uint256,uint256,uint256) { 641 | LOCK_INFO memory info = lock[account_]; 642 | return (info.lock_array.length, info.lock_array[0].release_time,info.lock_array[0].balance,block.timestamp); 643 | } 644 | 645 | function freezedBalanceOf(address account_) public view returns (uint256) { 646 | uint256 ret = 0 ; 647 | LOCK_INFO memory info = lock[account_]; 648 | LOCK_OBJ memory obj ; 649 | for(uint256 i = 0 ; i block.timestamp){ 652 | ret = ret.add(obj.balance); 653 | } 654 | } 655 | return ret; 656 | } 657 | function LockedInfoBalanceOf(address account_) internal view returns (uint256) { 658 | uint256 ret = 0 ; 659 | LOCK_INFO memory info = lock[account_]; 660 | LOCK_OBJ memory obj ; 661 | for(uint256 i = 0 ; i 0){ 664 | ret = ret.add(obj.balance); 665 | } 666 | } 667 | return ret; 668 | } 669 | 670 | function update_freezed_balances(address account_) internal { 671 | uint256 time = 0 ; 672 | LOCK_INFO memory info = lock[account_]; 673 | if(info.lock_array.length == 0){ 674 | return ; 675 | } 676 | uint256 ret = 0 ; 677 | LOCK_INFO storage sInfo = lock[account_]; 678 | for(uint256 i = 0 ; i 0 && time < block.timestamp){ 681 | sInfo.lock_array[i].release_time = 0; 682 | sInfo.lock_array[i].balance = 0; 683 | ret = ret.add(info.lock_array[i].balance); 684 | } 685 | } 686 | _add_balances(account_,ret); 687 | } 688 | 689 | function balanceOf(address account) public view virtual override returns (uint256) { 690 | return super.balanceOf(account).add(LockedInfoBalanceOf(account)); 691 | } 692 | 693 | function transfer(address recipient, uint256 amount) public virtual override returns (bool) { 694 | update_freezed_balances(msg.sender); 695 | super.transfer( recipient, amount); 696 | return true; 697 | } 698 | function burn(uint256 amount_) external virtual { 699 | _burn(msg.sender, amount_); 700 | } 701 | 702 | function burnFrom(address account_, uint256 amount_) external virtual { 703 | _burnFrom(account_, amount_); 704 | } 705 | 706 | function _burnFrom(address account_, uint256 amount_) internal virtual { 707 | uint256 decreasedAllowance_ = allowance(account_, msg.sender).sub(amount_); 708 | 709 | _approve(account_, msg.sender, decreasedAllowance_); 710 | _burn(account_, amount_); 711 | } 712 | 713 | function _beforeTokenTransfer( 714 | address from, 715 | address to, 716 | uint256 amount 717 | ) internal virtual override { 718 | super._beforeTokenTransfer(from, to, amount); 719 | require(!paused() || isTransferWhitelist[from], "ERC20Pausable: token transfer while paused"); 720 | } 721 | 722 | function addWhitelist(address account_) external onlyOwner { 723 | isTransferWhitelist[account_] = true; 724 | } 725 | 726 | function removeWhitelist(address account_) external onlyOwner { 727 | delete isTransferWhitelist[account_]; 728 | } 729 | 730 | function unpauseTransfer() external onlyOwner { 731 | _unpause(); 732 | } 733 | 734 | function pauseTransfer() external onlyOwner { 735 | _pause(); 736 | } 737 | 738 | function setFeeAddress(address feeAddress_) external onlyOwner { 739 | feeAddress = feeAddress_; 740 | } 741 | 742 | function maxSupply() public pure returns (uint256) { 743 | return CAP; 744 | } 745 | } 746 | -------------------------------------------------------------------------------- /contracts/StakingDistributor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | 3 | pragma solidity 0.7.5; 4 | 5 | library SafeERC20 { 6 | using SafeMath for uint256; 7 | using Address for address; 8 | 9 | function safeTransfer(IERC20 token, address to, uint256 value) internal { 10 | _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); 11 | } 12 | 13 | function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { 14 | _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); 15 | } 16 | 17 | function safeApprove(IERC20 token, address spender, uint256 value) internal { 18 | 19 | require((value == 0) || (token.allowance(address(this), spender) == 0), 20 | "SafeERC20: approve from non-zero to non-zero allowance" 21 | ); 22 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); 23 | } 24 | 25 | function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { 26 | uint256 newAllowance = token.allowance(address(this), spender).add(value); 27 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); 28 | } 29 | 30 | function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { 31 | uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); 32 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); 33 | } 34 | 35 | function _callOptionalReturn(IERC20 token, bytes memory data) private { 36 | 37 | bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); 38 | if (returndata.length > 0) { // Return data is optional 39 | // solhint-disable-next-line max-line-length 40 | require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); 41 | } 42 | } 43 | } 44 | 45 | library SafeMath { 46 | 47 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 48 | uint256 c = a + b; 49 | require(c >= a, "SafeMath: addition overflow"); 50 | 51 | return c; 52 | } 53 | 54 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 55 | return sub(a, b, "SafeMath: subtraction overflow"); 56 | } 57 | 58 | function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 59 | require(b <= a, errorMessage); 60 | uint256 c = a - b; 61 | 62 | return c; 63 | } 64 | 65 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 66 | 67 | if (a == 0) { 68 | return 0; 69 | } 70 | 71 | uint256 c = a * b; 72 | require(c / a == b, "SafeMath: multiplication overflow"); 73 | 74 | return c; 75 | } 76 | 77 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 78 | return div(a, b, "SafeMath: division by zero"); 79 | } 80 | 81 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 82 | require(b > 0, errorMessage); 83 | uint256 c = a / b; 84 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 85 | 86 | return c; 87 | } 88 | 89 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 90 | return mod(a, b, "SafeMath: modulo by zero"); 91 | } 92 | 93 | function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 94 | require(b != 0, errorMessage); 95 | return a % b; 96 | } 97 | 98 | // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method) 99 | function sqrrt(uint256 a) internal pure returns (uint c) { 100 | if (a > 3) { 101 | c = a; 102 | uint b = add( div( a, 2), 1 ); 103 | while (b < c) { 104 | c = b; 105 | b = div( add( div( a, b ), b), 2 ); 106 | } 107 | } else if (a != 0) { 108 | c = 1; 109 | } 110 | } 111 | 112 | function percentageAmount( uint256 total_, uint8 percentage_ ) internal pure returns ( uint256 percentAmount_ ) { 113 | return div( mul( total_, percentage_ ), 1000 ); 114 | } 115 | 116 | function substractPercentage( uint256 total_, uint8 percentageToSub_ ) internal pure returns ( uint256 result_ ) { 117 | return sub( total_, div( mul( total_, percentageToSub_ ), 1000 ) ); 118 | } 119 | 120 | function percentageOfTotal( uint256 part_, uint256 total_ ) internal pure returns ( uint256 percent_ ) { 121 | return div( mul(part_, 100) , total_ ); 122 | } 123 | 124 | function average(uint256 a, uint256 b) internal pure returns (uint256) { 125 | // (a + b) / 2 can overflow, so we distribute 126 | return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); 127 | } 128 | 129 | function quadraticPricing( uint256 payment_, uint256 multiplier_ ) internal pure returns (uint256) { 130 | return sqrrt( mul( multiplier_, payment_ ) ); 131 | } 132 | 133 | function bondingCurve( uint256 supply_, uint256 multiplier_ ) internal pure returns (uint256) { 134 | return mul( multiplier_, supply_ ); 135 | } 136 | } 137 | 138 | interface IERC20 { 139 | 140 | function totalSupply() external view returns (uint256); 141 | 142 | function balanceOf(address account) external view returns (uint256); 143 | 144 | function transfer(address recipient, uint256 amount) external returns (bool); 145 | 146 | function allowance(address owner, address spender) external view returns (uint256); 147 | 148 | function approve(address spender, uint256 amount) external returns (bool); 149 | 150 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 151 | 152 | event Transfer(address indexed from, address indexed to, uint256 value); 153 | 154 | event Approval(address indexed owner, address indexed spender, uint256 value); 155 | } 156 | 157 | library Address { 158 | 159 | function isContract(address account) internal view returns (bool) { 160 | // This method relies in extcodesize, which returns 0 for contracts in 161 | // construction, since the code is only stored at the end of the 162 | // constructor execution. 163 | 164 | uint256 size; 165 | // solhint-disable-next-line no-inline-assembly 166 | assembly { size := extcodesize(account) } 167 | return size > 0; 168 | } 169 | 170 | function sendValue(address payable recipient, uint256 amount) internal { 171 | require(address(this).balance >= amount, "Address: insufficient balance"); 172 | 173 | // solhint-disable-next-line avoid-low-level-calls, avoid-call-value 174 | (bool success, ) = recipient.call{ value: amount }(""); 175 | require(success, "Address: unable to send value, recipient may have reverted"); 176 | } 177 | 178 | function functionCall(address target, bytes memory data) internal returns (bytes memory) { 179 | return functionCall(target, data, "Address: low-level call failed"); 180 | } 181 | 182 | function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { 183 | return _functionCallWithValue(target, data, 0, errorMessage); 184 | } 185 | 186 | function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { 187 | return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); 188 | } 189 | 190 | function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { 191 | require(address(this).balance >= value, "Address: insufficient balance for call"); 192 | require(isContract(target), "Address: call to non-contract"); 193 | 194 | // solhint-disable-next-line avoid-low-level-calls 195 | (bool success, bytes memory returndata) = target.call{ value: value }(data); 196 | return _verifyCallResult(success, returndata, errorMessage); 197 | } 198 | 199 | function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) { 200 | require(isContract(target), "Address: call to non-contract"); 201 | 202 | // solhint-disable-next-line avoid-low-level-calls 203 | (bool success, bytes memory returndata) = target.call{ value: weiValue }(data); 204 | if (success) { 205 | return returndata; 206 | } else { 207 | // Look for revert reason and bubble it up if present 208 | if (returndata.length > 0) { 209 | // The easiest way to bubble the revert reason is using memory via assembly 210 | 211 | // solhint-disable-next-line no-inline-assembly 212 | assembly { 213 | let returndata_size := mload(returndata) 214 | revert(add(32, returndata), returndata_size) 215 | } 216 | } else { 217 | revert(errorMessage); 218 | } 219 | } 220 | } 221 | 222 | function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { 223 | return functionStaticCall(target, data, "Address: low-level static call failed"); 224 | } 225 | 226 | function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { 227 | require(isContract(target), "Address: static call to non-contract"); 228 | 229 | // solhint-disable-next-line avoid-low-level-calls 230 | (bool success, bytes memory returndata) = target.staticcall(data); 231 | return _verifyCallResult(success, returndata, errorMessage); 232 | } 233 | 234 | function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { 235 | return functionDelegateCall(target, data, "Address: low-level delegate call failed"); 236 | } 237 | 238 | function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { 239 | require(isContract(target), "Address: delegate call to non-contract"); 240 | (bool success, bytes memory returndata) = target.delegatecall(data); 241 | return _verifyCallResult(success, returndata, errorMessage); 242 | } 243 | 244 | function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { 245 | if (success) { 246 | return returndata; 247 | } else { 248 | if (returndata.length > 0) { 249 | assembly { 250 | let returndata_size := mload(returndata) 251 | revert(add(32, returndata), returndata_size) 252 | } 253 | } else { 254 | revert(errorMessage); 255 | } 256 | } 257 | } 258 | 259 | function addressToString(address _address) internal pure returns(string memory) { 260 | bytes32 _bytes = bytes32(uint256(_address)); 261 | bytes memory HEX = "0123456789abcdef"; 262 | bytes memory _addr = new bytes(42); 263 | 264 | _addr[0] = '0'; 265 | _addr[1] = 'x'; 266 | 267 | for(uint256 i = 0; i < 20; i++) { 268 | _addr[2+i*2] = HEX[uint8(_bytes[i + 12] >> 4)]; 269 | _addr[3+i*2] = HEX[uint8(_bytes[i + 12] & 0x0f)]; 270 | } 271 | 272 | return string(_addr); 273 | 274 | } 275 | } 276 | 277 | 278 | interface IPolicy { 279 | 280 | function policy() external view returns (address); 281 | 282 | function renouncePolicy() external; 283 | 284 | function pushPolicy( address newPolicy_ ) external; 285 | 286 | function pullPolicy() external; 287 | } 288 | 289 | contract Policy is IPolicy { 290 | 291 | address internal _policy; 292 | address internal _newPolicy; 293 | 294 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 295 | 296 | constructor () { 297 | _policy = msg.sender; 298 | emit OwnershipTransferred( address(0), _policy ); 299 | } 300 | 301 | function policy() public view override returns (address) { 302 | return _policy; 303 | } 304 | 305 | modifier onlyPolicy() { 306 | require( _policy == msg.sender, "Ownable: caller is not the owner" ); 307 | _; 308 | } 309 | 310 | function renouncePolicy() public virtual override onlyPolicy() { 311 | emit OwnershipTransferred( _policy, address(0) ); 312 | _policy = address(0); 313 | } 314 | 315 | function pushPolicy( address newPolicy_ ) public virtual override onlyPolicy() { 316 | require( newPolicy_ != address(0), "Ownable: new owner is the zero address"); 317 | _newPolicy = newPolicy_; 318 | } 319 | 320 | function pullPolicy() public virtual override { 321 | require( msg.sender == _newPolicy ); 322 | emit OwnershipTransferred( _policy, _newPolicy ); 323 | _policy = _newPolicy; 324 | } 325 | } 326 | 327 | interface ITreasury { 328 | function mintRewards( address _recipient, uint _amount ) external; 329 | } 330 | 331 | contract Distributor is Policy { 332 | using SafeMath for uint; 333 | using SafeERC20 for IERC20; 334 | 335 | 336 | 337 | /* ====== VARIABLES ====== */ 338 | 339 | address public immutable SYNASSETS; 340 | address public immutable treasury; 341 | 342 | uint public immutable epochLength; 343 | uint public nextEpochBlock; 344 | 345 | mapping( uint => Adjust ) public adjustments; 346 | 347 | 348 | /* ====== STRUCTS ====== */ 349 | 350 | struct Info { 351 | uint rate; // in ten-thousandths ( 5000 = 0.5% ) 352 | address recipient; 353 | } 354 | Info[] public info; 355 | 356 | struct Adjust { 357 | bool add; 358 | uint rate; 359 | uint target; 360 | } 361 | 362 | 363 | 364 | /* ====== CONSTRUCTOR ====== */ 365 | 366 | constructor( address _treasury, address _SYNASSETS, uint _epochLength, uint _nextEpochBlock ) { 367 | require( _treasury != address(0) ); 368 | treasury = _treasury; 369 | require( _SYNASSETS != address(0) ); 370 | SYNASSETS = _SYNASSETS; 371 | epochLength = _epochLength; 372 | nextEpochBlock = _nextEpochBlock; 373 | } 374 | 375 | 376 | 377 | /* ====== PUBLIC FUNCTIONS ====== */ 378 | 379 | /** 380 | @notice send epoch reward to staking contract 381 | */ 382 | function distribute() external returns ( bool ) { 383 | if ( nextEpochBlock <= block.number ) { 384 | nextEpochBlock = nextEpochBlock.add( epochLength ); // set next epoch block 385 | 386 | // distribute rewards to each recipient 387 | for ( uint i = 0; i < info.length; i++ ) { 388 | if ( info[ i ].rate > 0 ) { 389 | ITreasury( treasury ).mintRewards( // mint and send from treasury 390 | info[ i ].recipient, 391 | nextRewardAt( info[ i ].rate ) 392 | ); 393 | adjust( i ); // check for adjustment 394 | } 395 | } 396 | return true; 397 | } else { 398 | return false; 399 | } 400 | } 401 | 402 | 403 | 404 | /* ====== INTERNAL FUNCTIONS ====== */ 405 | 406 | /** 407 | @notice increment reward rate for collector 408 | */ 409 | function adjust( uint _index ) internal { 410 | Adjust memory adjustment = adjustments[ _index ]; 411 | if ( adjustment.rate != 0 ) { 412 | if ( adjustment.add ) { // if rate should increase 413 | info[ _index ].rate = info[ _index ].rate.add( adjustment.rate ); // raise rate 414 | if ( info[ _index ].rate >= adjustment.target ) { // if target met 415 | adjustments[ _index ].rate = 0; // turn off adjustment 416 | } 417 | } else { // if rate should decrease 418 | info[ _index ].rate = info[ _index ].rate.sub( adjustment.rate ); // lower rate 419 | if ( info[ _index ].rate <= adjustment.target ) { // if target met 420 | adjustments[ _index ].rate = 0; // turn off adjustment 421 | } 422 | } 423 | } 424 | } 425 | 426 | 427 | 428 | /* ====== VIEW FUNCTIONS ====== */ 429 | 430 | /** 431 | @notice view function for next reward at given rate 432 | @param _rate uint 433 | @return uint 434 | */ 435 | function nextRewardAt( uint _rate ) public view returns ( uint ) { 436 | return IERC20( SYNASSETS ).totalSupply().mul( _rate ).div( 1000000 ); 437 | } 438 | 439 | /** 440 | @notice view function for next reward for specified address 441 | @param _recipient address 442 | @return uint 443 | */ 444 | function nextRewardFor( address _recipient ) public view returns ( uint ) { 445 | uint reward; 446 | for ( uint i = 0; i < info.length; i++ ) { 447 | if ( info[ i ].recipient == _recipient ) { 448 | reward = nextRewardAt( info[ i ].rate ); 449 | } 450 | } 451 | return reward; 452 | } 453 | 454 | 455 | 456 | /* ====== POLICY FUNCTIONS ====== */ 457 | 458 | /** 459 | @notice adds recipient for distributions 460 | @param _recipient address 461 | @param _rewardRate uint 462 | */ 463 | function addRecipient( address _recipient, uint _rewardRate ) external onlyPolicy() { 464 | require( _recipient != address(0) ); 465 | info.push( Info({ 466 | recipient: _recipient, 467 | rate: _rewardRate 468 | })); 469 | } 470 | 471 | /** 472 | @notice removes recipient for distributions 473 | @param _index uint 474 | @param _recipient address 475 | */ 476 | function removeRecipient( uint _index, address _recipient ) external onlyPolicy() { 477 | require( _recipient == info[ _index ].recipient ); 478 | info[ _index ].recipient = address(0); 479 | info[ _index ].rate = 0; 480 | } 481 | 482 | /** 483 | @notice set adjustment info for a collector's reward rate 484 | @param _index uint 485 | @param _add bool 486 | @param _rate uint 487 | @param _target uint 488 | */ 489 | function setAdjustment( uint _index, bool _add, uint _rate, uint _target ) external onlyPolicy() { 490 | adjustments[ _index ] = Adjust({ 491 | add: _add, 492 | rate: _rate, 493 | target: _target 494 | }); 495 | } 496 | } 497 | -------------------------------------------------------------------------------- /contracts/StakingHelper.sol: -------------------------------------------------------------------------------- 1 | /** 2 | *Submitted for verification at Etherscan.io on 2021-06-20 3 | */ 4 | 5 | /** 6 | *Submitted for verification at Etherscan.io on 2021-06-12 7 | */ 8 | 9 | // SPDX-License-Identifier: AGPL-3.0-or-later 10 | pragma solidity 0.7.5; 11 | 12 | 13 | interface IERC20 { 14 | function decimals() external view returns (uint8); 15 | /** 16 | * @dev Returns the amount of tokens in existence. 17 | */ 18 | function totalSupply() external view returns (uint256); 19 | 20 | /** 21 | * @dev Returns the amount of tokens owned by `account`. 22 | */ 23 | function balanceOf(address account) external view returns (uint256); 24 | 25 | /** 26 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 27 | * 28 | * Returns a boolean value indicating whether the operation succeeded. 29 | * 30 | * Emits a {Transfer} event. 31 | */ 32 | function transfer(address recipient, uint256 amount) external returns (bool); 33 | 34 | /** 35 | * @dev Returns the remaining number of tokens that `spender` will be 36 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 37 | * zero by default. 38 | * 39 | * This value changes when {approve} or {transferFrom} are called. 40 | */ 41 | function allowance(address owner, address spender) external view returns (uint256); 42 | 43 | /** 44 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 45 | * 46 | * Returns a boolean value indicating whether the operation succeeded. 47 | * 48 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 49 | * that someone may use both the old and the new allowance by unfortunate 50 | * transaction ordering. One possible solution to mitigate this race 51 | * condition is to first reduce the spender's allowance to 0 and set the 52 | * desired value afterwards: 53 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 54 | * 55 | * Emits an {Approval} event. 56 | */ 57 | function approve(address spender, uint256 amount) external returns (bool); 58 | 59 | /** 60 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 61 | * allowance mechanism. `amount` is then deducted from the caller's 62 | * allowance. 63 | * 64 | * Returns a boolean value indicating whether the operation succeeded. 65 | * 66 | * Emits a {Transfer} event. 67 | */ 68 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 69 | 70 | /** 71 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 72 | * another (`to`). 73 | * 74 | * Note that `value` may be zero. 75 | */ 76 | event Transfer(address indexed from, address indexed to, uint256 value); 77 | 78 | /** 79 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 80 | * a call to {approve}. `value` is the new allowance. 81 | */ 82 | event Approval(address indexed owner, address indexed spender, uint256 value); 83 | } 84 | 85 | interface IStaking { 86 | function stake( uint _amount, address _recipient ) external returns ( bool ); 87 | function claim( address _recipient, address inviter ) external; 88 | } 89 | 90 | contract StakingHelper { 91 | 92 | address public immutable staking; 93 | address public immutable SYNASSETS; 94 | 95 | constructor ( address _staking, address _SYNASSETS ) { 96 | require( _staking != address(0) ); 97 | staking = _staking; 98 | require( _SYNASSETS != address(0) ); 99 | SYNASSETS = _SYNASSETS; 100 | } 101 | 102 | function stake( uint _amount, address _recipient, address inviter ) external { 103 | IERC20( SYNASSETS ).transferFrom( msg.sender, address(this), _amount ); 104 | IERC20( SYNASSETS ).approve( staking, _amount ); 105 | IStaking( staking ).stake( _amount, _recipient ); 106 | IStaking( staking ).claim( _recipient, inviter ); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /contracts/StakingWarmup.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.7.5; 3 | 4 | 5 | interface IERC20 { 6 | function decimals() external view returns (uint8); 7 | /** 8 | * @dev Returns the amount of tokens in existence. 9 | */ 10 | function totalSupply() external view returns (uint256); 11 | 12 | /** 13 | * @dev Returns the amount of tokens owned by `account`. 14 | */ 15 | function balanceOf(address account) external view returns (uint256); 16 | 17 | /** 18 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 19 | * 20 | * Returns a boolean value indicating whether the operation succeeded. 21 | * 22 | * Emits a {Transfer} event. 23 | */ 24 | function transfer(address recipient, uint256 amount) external returns (bool); 25 | 26 | /** 27 | * @dev Returns the remaining number of tokens that `spender` will be 28 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 29 | * zero by default. 30 | * 31 | * This value changes when {approve} or {transferFrom} are called. 32 | */ 33 | function allowance(address owner, address spender) external view returns (uint256); 34 | 35 | /** 36 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 37 | * 38 | * Returns a boolean value indicating whether the operation succeeded. 39 | * 40 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 41 | * that someone may use both the old and the new allowance by unfortunate 42 | * transaction ordering. One possible solution to mitigate this race 43 | * condition is to first reduce the spender's allowance to 0 and set the 44 | * desired value afterwards: 45 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 46 | * 47 | * Emits an {Approval} event. 48 | */ 49 | function approve(address spender, uint256 amount) external returns (bool); 50 | 51 | /** 52 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 53 | * allowance mechanism. `amount` is then deducted from the caller's 54 | * allowance. 55 | * 56 | * Returns a boolean value indicating whether the operation succeeded. 57 | * 58 | * Emits a {Transfer} event. 59 | */ 60 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 61 | 62 | /** 63 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 64 | * another (`to`). 65 | * 66 | * Note that `value` may be zero. 67 | */ 68 | event Transfer(address indexed from, address indexed to, uint256 value); 69 | 70 | /** 71 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 72 | * a call to {approve}. `value` is the new allowance. 73 | */ 74 | event Approval(address indexed owner, address indexed spender, uint256 value); 75 | } 76 | 77 | contract StakingWarmup { 78 | 79 | address public immutable staking; 80 | address public immutable sSYNASSETS; 81 | 82 | constructor ( address _staking, address _sSYNASSETS ) { 83 | require( _staking != address(0) ); 84 | staking = _staking; 85 | require( _sSYNASSETS != address(0) ); 86 | sSYNASSETS = _sSYNASSETS; 87 | } 88 | 89 | function retrieve( address _staker, uint _amount ) external { 90 | require( msg.sender == staking ); 91 | IERC20( sSYNASSETS ).transfer( _staker, _amount ); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /contracts/StandardBondingCalculator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.7.5; 3 | 4 | library FullMath { 5 | function fullMul(uint256 x, uint256 y) private pure returns (uint256 l, uint256 h) { 6 | uint256 mm = mulmod(x, y, uint256(-1)); 7 | l = x * y; 8 | h = mm - l; 9 | if (mm < l) h -= 1; 10 | } 11 | 12 | function fullDiv( 13 | uint256 l, 14 | uint256 h, 15 | uint256 d 16 | ) private pure returns (uint256) { 17 | uint256 pow2 = d & -d; 18 | d /= pow2; 19 | l /= pow2; 20 | l += h * ((-pow2) / pow2 + 1); 21 | uint256 r = 1; 22 | r *= 2 - d * r; 23 | r *= 2 - d * r; 24 | r *= 2 - d * r; 25 | r *= 2 - d * r; 26 | r *= 2 - d * r; 27 | r *= 2 - d * r; 28 | r *= 2 - d * r; 29 | r *= 2 - d * r; 30 | return l * r; 31 | } 32 | 33 | function mulDiv( 34 | uint256 x, 35 | uint256 y, 36 | uint256 d 37 | ) internal pure returns (uint256) { 38 | (uint256 l, uint256 h) = fullMul(x, y); 39 | uint256 mm = mulmod(x, y, d); 40 | if (mm > l) h -= 1; 41 | l -= mm; 42 | require(h < d, 'FullMath::mulDiv: overflow'); 43 | return fullDiv(l, h, d); 44 | } 45 | } 46 | 47 | library Babylonian { 48 | 49 | function sqrt(uint256 x) internal pure returns (uint256) { 50 | if (x == 0) return 0; 51 | 52 | uint256 xx = x; 53 | uint256 r = 1; 54 | if (xx >= 0x100000000000000000000000000000000) { 55 | xx >>= 128; 56 | r <<= 64; 57 | } 58 | if (xx >= 0x10000000000000000) { 59 | xx >>= 64; 60 | r <<= 32; 61 | } 62 | if (xx >= 0x100000000) { 63 | xx >>= 32; 64 | r <<= 16; 65 | } 66 | if (xx >= 0x10000) { 67 | xx >>= 16; 68 | r <<= 8; 69 | } 70 | if (xx >= 0x100) { 71 | xx >>= 8; 72 | r <<= 4; 73 | } 74 | if (xx >= 0x10) { 75 | xx >>= 4; 76 | r <<= 2; 77 | } 78 | if (xx >= 0x8) { 79 | r <<= 1; 80 | } 81 | r = (r + x / r) >> 1; 82 | r = (r + x / r) >> 1; 83 | r = (r + x / r) >> 1; 84 | r = (r + x / r) >> 1; 85 | r = (r + x / r) >> 1; 86 | r = (r + x / r) >> 1; 87 | r = (r + x / r) >> 1; // Seven iterations should be enough 88 | uint256 r1 = x / r; 89 | return (r < r1 ? r : r1); 90 | } 91 | } 92 | 93 | library BitMath { 94 | 95 | function mostSignificantBit(uint256 x) internal pure returns (uint8 r) { 96 | require(x > 0, 'BitMath::mostSignificantBit: zero'); 97 | 98 | if (x >= 0x100000000000000000000000000000000) { 99 | x >>= 128; 100 | r += 128; 101 | } 102 | if (x >= 0x10000000000000000) { 103 | x >>= 64; 104 | r += 64; 105 | } 106 | if (x >= 0x100000000) { 107 | x >>= 32; 108 | r += 32; 109 | } 110 | if (x >= 0x10000) { 111 | x >>= 16; 112 | r += 16; 113 | } 114 | if (x >= 0x100) { 115 | x >>= 8; 116 | r += 8; 117 | } 118 | if (x >= 0x10) { 119 | x >>= 4; 120 | r += 4; 121 | } 122 | if (x >= 0x4) { 123 | x >>= 2; 124 | r += 2; 125 | } 126 | if (x >= 0x2) r += 1; 127 | } 128 | } 129 | 130 | library FixedPoint { 131 | // range: [0, 2**112 - 1] 132 | // resolution: 1 / 2**112 133 | struct uq112x112 { 134 | uint224 _x; 135 | } 136 | 137 | // range: [0, 2**144 - 1] 138 | // resolution: 1 / 2**112 139 | struct uq144x112 { 140 | uint256 _x; 141 | } 142 | 143 | uint8 private constant RESOLUTION = 112; 144 | uint256 private constant Q112 = 0x10000000000000000000000000000; 145 | uint256 private constant Q224 = 0x100000000000000000000000000000000000000000000000000000000; 146 | uint256 private constant LOWER_MASK = 0xffffffffffffffffffffffffffff; // decimal of UQ*x112 (lower 112 bits) 147 | 148 | // decode a UQ112x112 into a uint112 by truncating after the radix point 149 | function decode(uq112x112 memory self) internal pure returns (uint112) { 150 | return uint112(self._x >> RESOLUTION); 151 | } 152 | 153 | // decode a uq112x112 into a uint with 18 decimals of precision 154 | function decode112with18(uq112x112 memory self) internal pure returns (uint) { 155 | return uint(self._x) / 5192296858534827; 156 | } 157 | 158 | function fraction(uint256 numerator, uint256 denominator) internal pure returns (uq112x112 memory) { 159 | require(denominator > 0, 'FixedPoint::fraction: division by zero'); 160 | if (numerator == 0) return FixedPoint.uq112x112(0); 161 | 162 | if (numerator <= uint144(-1)) { 163 | uint256 result = (numerator << RESOLUTION) / denominator; 164 | require(result <= uint224(-1), 'FixedPoint::fraction: overflow'); 165 | return uq112x112(uint224(result)); 166 | } else { 167 | uint256 result = FullMath.mulDiv(numerator, Q112, denominator); 168 | require(result <= uint224(-1), 'FixedPoint::fraction: overflow'); 169 | return uq112x112(uint224(result)); 170 | } 171 | } 172 | 173 | // square root of a UQ112x112 174 | // lossy between 0/1 and 40 bits 175 | function sqrt(uq112x112 memory self) internal pure returns (uq112x112 memory) { 176 | if (self._x <= uint144(-1)) { 177 | return uq112x112(uint224(Babylonian.sqrt(uint256(self._x) << 112))); 178 | } 179 | 180 | uint8 safeShiftBits = 255 - BitMath.mostSignificantBit(self._x); 181 | safeShiftBits -= safeShiftBits % 2; 182 | return uq112x112(uint224(Babylonian.sqrt(uint256(self._x) << safeShiftBits) << ((112 - safeShiftBits) / 2))); 183 | } 184 | } 185 | 186 | library SafeMath { 187 | 188 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 189 | uint256 c = a + b; 190 | require(c >= a, "SafeMath: addition overflow"); 191 | 192 | return c; 193 | } 194 | 195 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 196 | return sub(a, b, "SafeMath: subtraction overflow"); 197 | } 198 | 199 | function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 200 | require(b <= a, errorMessage); 201 | uint256 c = a - b; 202 | 203 | return c; 204 | } 205 | 206 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 207 | 208 | if (a == 0) { 209 | return 0; 210 | } 211 | 212 | uint256 c = a * b; 213 | require(c / a == b, "SafeMath: multiplication overflow"); 214 | 215 | return c; 216 | } 217 | 218 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 219 | return div(a, b, "SafeMath: division by zero"); 220 | } 221 | 222 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 223 | require(b > 0, errorMessage); 224 | uint256 c = a / b; 225 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 226 | 227 | return c; 228 | } 229 | 230 | function sqrrt(uint256 a) internal pure returns (uint c) { 231 | if (a > 3) { 232 | c = a; 233 | uint b = add( div( a, 2), 1 ); 234 | while (b < c) { 235 | c = b; 236 | b = div( add( div( a, b ), b), 2 ); 237 | } 238 | } else if (a != 0) { 239 | c = 1; 240 | } 241 | } 242 | } 243 | 244 | interface IERC20 { 245 | function decimals() external view returns (uint8); 246 | } 247 | 248 | interface IUniswapV2ERC20 { 249 | function totalSupply() external view returns (uint); 250 | } 251 | 252 | interface IUniswapV2Pair is IUniswapV2ERC20 { 253 | function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); 254 | function token0() external view returns ( address ); 255 | function token1() external view returns ( address ); 256 | } 257 | 258 | interface IBondingCalculator { 259 | function valuation( address pair_, uint amount_ ) external view returns ( uint _value ); 260 | } 261 | 262 | contract SynassetsBondingCalculator is IBondingCalculator { 263 | 264 | using FixedPoint for *; 265 | using SafeMath for uint; 266 | using SafeMath for uint112; 267 | 268 | address public immutable SYNASSETS; 269 | 270 | constructor( address _SYNASSETS ) { 271 | require( _SYNASSETS != address(0) ); 272 | SYNASSETS = _SYNASSETS; 273 | } 274 | 275 | function getKValue( address _pair ) public view returns( uint k_ ) { 276 | uint token0 = IERC20( IUniswapV2Pair( _pair ).token0() ).decimals(); 277 | uint token1 = IERC20( IUniswapV2Pair( _pair ).token1() ).decimals(); 278 | uint decimals = token0.add( token1 ).sub( IERC20( _pair ).decimals() ); 279 | 280 | (uint reserve0, uint reserve1, ) = IUniswapV2Pair( _pair ).getReserves(); 281 | k_ = reserve0.mul(reserve1).div( 10 ** decimals ); 282 | } 283 | 284 | function getTotalValue( address _pair ) public view returns ( uint _value ) { 285 | _value = getKValue( _pair ).sqrrt().mul(2); 286 | } 287 | 288 | function valuation( address _pair, uint amount_ ) external view override returns ( uint _value ) { 289 | uint totalValue = getTotalValue( _pair ); 290 | uint totalSupply = IUniswapV2Pair( _pair ).totalSupply(); 291 | 292 | _value = totalValue.mul( FixedPoint.fraction( amount_, totalSupply ).decode112with18() ).div( 1e18 ); 293 | } 294 | 295 | function markdown( address _pair ) external view returns ( uint ) { 296 | ( uint reserve0, uint reserve1, ) = IUniswapV2Pair( _pair ).getReserves(); 297 | 298 | uint reserve; 299 | if ( IUniswapV2Pair( _pair ).token0() == SYNASSETS ) { 300 | reserve = reserve1; 301 | } else { 302 | reserve = reserve0; 303 | } 304 | return reserve.mul( 2 * ( 10 ** IERC20( SYNASSETS ).decimals() ) ).div( getTotalValue( _pair ) ); 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /contracts/VaultTmp.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.7.5; 3 | 4 | interface IERC20 { 5 | /** 6 | * @dev Returns the amount of tokens in existence. 7 | */ 8 | function totalSupply() external view returns (uint256); 9 | 10 | /** 11 | * @dev Returns the amount of tokens owned by `account`. 12 | */ 13 | function balanceOf(address account) external view returns (uint256); 14 | 15 | /** 16 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 17 | * 18 | * Returns a boolean value indicating whether the operation succeeded. 19 | * 20 | * Emits a {Transfer} event. 21 | */ 22 | function transfer(address recipient, uint256 amount) external returns (bool); 23 | 24 | /** 25 | * @dev Returns the remaining number of tokens that `spender` will be 26 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 27 | * zero by default. 28 | * 29 | * This value changes when {approve} or {transferFrom} are called. 30 | */ 31 | function allowance(address owner, address spender) external view returns (uint256); 32 | 33 | /** 34 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 35 | * 36 | * Returns a boolean value indicating whether the operation succeeded. 37 | * 38 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 39 | * that someone may use both the old and the new allowance by unfortunate 40 | * transaction ordering. One possible solution to mitigate this race 41 | * condition is to first reduce the spender's allowance to 0 and set the 42 | * desired value afterwards: 43 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 44 | * 45 | * Emits an {Approval} event. 46 | */ 47 | function approve(address spender, uint256 amount) external returns (bool); 48 | 49 | /** 50 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 51 | * allowance mechanism. `amount` is then deducted from the caller's 52 | * allowance. 53 | * 54 | * Returns a boolean value indicating whether the operation succeeded. 55 | * 56 | * Emits a {Transfer} event. 57 | */ 58 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 59 | 60 | /** 61 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 62 | * another (`to`). 63 | * 64 | * Note that `value` may be zero. 65 | */ 66 | event Transfer(address indexed from, address indexed to, uint256 value); 67 | 68 | /** 69 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 70 | * a call to {approve}. `value` is the new allowance. 71 | */ 72 | event Approval(address indexed owner, address indexed spender, uint256 value); 73 | } 74 | 75 | contract VaultTmp { 76 | 77 | address public immutable SYNASSETS; 78 | 79 | constructor ( address _SYNASSETS ) { 80 | require( _SYNASSETS != address(0) ); 81 | SYNASSETS = _SYNASSETS; 82 | } 83 | 84 | function withdraw(address token) external { 85 | require(msg.sender == SYNASSETS); 86 | require( 87 | IERC20(token).transfer(msg.sender, IERC20(token).balanceOf(address(this))) 88 | ); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /contracts/mock/DAI.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.5; 2 | 3 | 4 | contract LibNote { 5 | event LogNote( 6 | bytes4 indexed sig, 7 | address indexed usr, 8 | bytes32 indexed arg1, 9 | bytes32 indexed arg2, 10 | bytes data 11 | ) anonymous; 12 | 13 | modifier note { 14 | _; 15 | // assembly { 16 | // // log an 'anonymous' event with a constant 6 words of calldata 17 | // // and four indexed topics: selector, caller, arg1 and arg2 18 | // let mark := msize() // end of memory ensures zero 19 | // mstore(0x40, add(mark, 288)) // update free memory pointer 20 | // mstore(mark, 0x20) // bytes type data offset 21 | // mstore(add(mark, 0x20), 224) // bytes size (padded) 22 | // calldatacopy(add(mark, 0x40), 0, 224) // bytes payload 23 | // log4(mark, 288, // calldata 24 | // shl(224, shr(224, calldataload(0))), // msg.sig 25 | // caller(), // msg.sender 26 | // calldataload(4), // arg1 27 | // calldataload(36) // arg2 28 | // ) 29 | // } 30 | } 31 | } 32 | 33 | interface IDAI { 34 | 35 | 36 | // --- Auth --- 37 | function wards() external returns ( uint256 ); 38 | 39 | function rely(address guy) external; 40 | 41 | function deny(address guy) external; 42 | 43 | // --- Token --- 44 | function transfer(address dst, uint wad) external returns (bool); 45 | 46 | function transferFrom(address src, address dst, uint wad) external returns (bool); 47 | 48 | function mint(address usr, uint wad) external; 49 | 50 | function burn(address usr, uint wad) external; 51 | 52 | function approve(address usr, uint wad) external returns (bool); 53 | 54 | // --- Alias --- 55 | function push(address usr, uint wad) external; 56 | 57 | function pull(address usr, uint wad) external; 58 | 59 | function move(address src, address dst, uint wad) external; 60 | 61 | // --- Approve by signature --- 62 | function permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s) external; 63 | } 64 | 65 | ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol 66 | // Copyright (C) 2017, 2018, 2019 dbrock, rain, mrchico 67 | 68 | // This program is free software: you can redistribute it and/or modify 69 | // it under the terms of the GNU Affero General Public License as published by 70 | // the Free Software Foundation, either version 3 of the License, or 71 | // (at your option) any later version. 72 | // 73 | // This program is distributed in the hope that it will be useful, 74 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 75 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 76 | // GNU Affero General Public License for more details. 77 | // 78 | // You should have received a copy of the GNU Affero General Public License 79 | // along with this program. If not, see . 80 | 81 | /* pragma solidity 0.5.12; */ 82 | 83 | /* import "./lib.sol"; */ 84 | 85 | contract DAI is LibNote { 86 | 87 | event Approval(address indexed src, address indexed guy, uint wad); 88 | event Transfer(address indexed src, address indexed dst, uint wad); 89 | 90 | // --- Auth --- 91 | mapping (address => uint) public wards; 92 | 93 | function rely(address guy) external note auth { wards[guy] = 1; } 94 | 95 | function deny(address guy) external note auth { wards[guy] = 0; } 96 | 97 | modifier auth { 98 | require(wards[msg.sender] == 1, "Dai/not-authorized"); 99 | _; 100 | } 101 | 102 | // --- ERC20 Data --- 103 | string public constant name = "TUSDC"; 104 | string public constant symbol = "TUSDC"; 105 | string public constant version = "1"; 106 | uint8 public constant decimals = 18; 107 | uint256 public totalSupply; 108 | uint public dailyDAILimit; 109 | 110 | mapping (address => uint) public balanceOf; 111 | mapping (address => mapping (address => uint)) private allowances; 112 | mapping (address => uint) public nonces; 113 | mapping (address => uint) public lastMintRestart; 114 | mapping (address => uint) public daiMintedToday; 115 | 116 | // event Approval(address indexed src, address indexed guy, uint wad); 117 | // event Transfer(address indexed src, address indexed dst, uint wad); 118 | 119 | // --- Math --- 120 | function add(uint x, uint y) internal pure returns (uint z) { 121 | require((z = x + y) >= x); 122 | } 123 | 124 | function sub(uint x, uint y) internal pure returns (uint z) { 125 | require((z = x - y) <= x); 126 | } 127 | 128 | // --- EIP712 niceties --- 129 | bytes32 public DOMAIN_SEPARATOR; 130 | // bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)"); 131 | bytes32 public constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb; 132 | 133 | constructor(uint256 chainId_) { 134 | wards[msg.sender] = 1; 135 | DOMAIN_SEPARATOR = keccak256(abi.encode( 136 | keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), 137 | keccak256(bytes(name)), 138 | keccak256(bytes(version)), 139 | chainId_, 140 | address(this) 141 | )); 142 | dailyDAILimit = 10000000000000000000000; 143 | } 144 | 145 | function allowance( address account_, address sender_ ) external view returns ( uint ) { 146 | return _allowance( account_, sender_ ); 147 | } 148 | 149 | function _allowance( address account_, address sender_ ) internal view returns ( uint ) { 150 | 151 | return allowances[account_][sender_]; 152 | } 153 | 154 | // --- Token --- 155 | function transfer(address dst, uint wad) external returns (bool) { 156 | return transferFrom(msg.sender, dst, wad); 157 | } 158 | 159 | function transferFrom(address src, address dst, uint wad) public returns (bool) { 160 | 161 | 162 | require(balanceOf[src] >= wad, "Dai/insufficient-balance"); 163 | if (src != msg.sender && _allowance( src, msg.sender ) != uint(-1)) { 164 | require(_allowance( src, msg.sender ) >= wad, "Dai/insufficient-allowance"); 165 | allowances[src][msg.sender] = sub(_allowance( src, msg.sender ), wad); 166 | } 167 | balanceOf[src] = sub(balanceOf[src], wad); 168 | balanceOf[dst] = add(balanceOf[dst], wad); 169 | emit Transfer(src, dst, wad); 170 | return true; 171 | } 172 | 173 | function addAuth(address usr) external auth { 174 | wards[usr] = 1; 175 | } 176 | 177 | function adjustDailyDAILimit(uint _limit) external auth { 178 | dailyDAILimit = _limit; 179 | } 180 | 181 | function mint(address usr, uint wad) external { 182 | 183 | if(wards[msg.sender] == 0) { 184 | require(add(wad, daiMintedToday[msg.sender]) <= dailyDAILimit || sub(block.number, lastMintRestart[msg.sender]) >= 6500 && wad <= dailyDAILimit, "Over daily DAI Limit"); 185 | if( sub(block.number, lastMintRestart[msg.sender]) >= 6500 ) { 186 | daiMintedToday[msg.sender] = wad; 187 | lastMintRestart[msg.sender] = block.number; 188 | } else { 189 | daiMintedToday[msg.sender] = add(daiMintedToday[msg.sender], wad); 190 | } 191 | } 192 | 193 | balanceOf[usr] = add(balanceOf[usr], wad); 194 | 195 | totalSupply = add(totalSupply, wad); 196 | 197 | 198 | emit Transfer(address(0), usr, wad); 199 | } 200 | 201 | function burn(address usr, uint wad) external { 202 | require(balanceOf[usr] >= wad, "Dai/insufficient-balance"); 203 | if (usr != msg.sender && _allowance( usr, msg.sender ) != uint(-1)) { 204 | require(_allowance( usr, msg.sender ) >= wad, "Dai/insufficient-allowance"); 205 | allowances[usr][msg.sender] = sub(_allowance( usr, msg.sender ), wad); 206 | } 207 | balanceOf[usr] = sub(balanceOf[usr], wad); 208 | totalSupply = sub(totalSupply, wad); 209 | emit Transfer(usr, address(0), wad); 210 | } 211 | 212 | function _approve(address usr, uint wad) internal returns (bool) { 213 | 214 | allowances[msg.sender][usr] = wad; 215 | 216 | emit Approval(msg.sender, usr, wad); 217 | return true; 218 | } 219 | 220 | function approve(address usr_, uint wad_ ) external returns (bool) { 221 | 222 | return _approve( usr_, wad_ ) ; 223 | } 224 | 225 | // --- Alias --- 226 | function push(address usr, uint wad) external { 227 | transferFrom(msg.sender, usr, wad); 228 | } 229 | 230 | function pull(address usr, uint wad) external { 231 | transferFrom(usr, msg.sender, wad); 232 | } 233 | 234 | function move(address src, address dst, uint wad) external { 235 | transferFrom(src, dst, wad); 236 | } 237 | 238 | // --- Approve by signature --- 239 | function permit(address holder, address spender, uint256 nonce, uint256 expiry, 240 | bool allowed, uint8 v, bytes32 r, bytes32 s) external 241 | { 242 | bytes32 digest = 243 | keccak256(abi.encodePacked( 244 | "\x19\x01", 245 | DOMAIN_SEPARATOR, 246 | keccak256(abi.encode(PERMIT_TYPEHASH, 247 | holder, 248 | spender, 249 | nonce, 250 | expiry, 251 | allowed)) 252 | )); 253 | 254 | require(holder != address(0), "Dai/invalid-address-0"); 255 | require(holder == ecrecover(digest, v, r, s), "Dai/invalid-permit"); 256 | require(expiry == 0 || block.timestamp <= expiry, "Dai/permit-expired"); 257 | require(nonce == nonces[holder]++, "Dai/invalid-nonce"); 258 | uint wad = allowed ? uint(-1) : 0; 259 | allowances[holder][spender] = wad; 260 | emit Approval(holder, spender, wad); 261 | } 262 | } -------------------------------------------------------------------------------- /contracts/mock/MockSAT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.7.5; 3 | 4 | /** 5 | * @title Initializable 6 | * 7 | * @dev Helper contract to support initializer functions. To use it, replace 8 | * the constructor with a function that has the `initializer` modifier. 9 | * WARNING: Unlike constructors, initializer functions must be manually 10 | * invoked. This applies both to deploying an Initializable contract, as well 11 | * as extending an Initializable contract via inheritance. 12 | * WARNING: When used with inheritance, manual care must be taken to not invoke 13 | * a parent initializer twice, or ensure that all initializers are idempotent, 14 | * because this is not dealt with automatically as with constructors. 15 | */ 16 | contract Initializable { 17 | 18 | /** 19 | * @dev Indicates that the contract has been initialized. 20 | */ 21 | bool private initialized; 22 | 23 | /** 24 | * @dev Indicates that the contract is in the process of being initialized. 25 | */ 26 | bool private initializing; 27 | 28 | /** 29 | * @dev Modifier to use in the initializer function of a contract. 30 | */ 31 | modifier initializer() { 32 | require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized"); 33 | 34 | bool isTopLevelCall = !initializing; 35 | if (isTopLevelCall) { 36 | initializing = true; 37 | initialized = true; 38 | } 39 | 40 | _; 41 | 42 | if (isTopLevelCall) { 43 | initializing = false; 44 | } 45 | } 46 | 47 | /// @dev Returns true if and only if the function is running in the constructor 48 | function isConstructor() private view returns (bool) { 49 | // extcodesize checks the size of the code stored in an address, and 50 | // address returns the current address. Since the code is still not 51 | // deployed when running a constructor, any checks on its code size will 52 | // yield zero, making it an effective way to detect if a contract is 53 | // under construction or not. 54 | address self = address(this); 55 | uint256 cs; 56 | assembly { cs := extcodesize(self) } 57 | return cs == 0; 58 | } 59 | } 60 | 61 | interface IERC20 { 62 | /** 63 | * @dev Returns the amount of tokens in existence. 64 | */ 65 | function totalSupply() external view returns (uint256); 66 | 67 | /** 68 | * @dev Returns the amount of tokens owned by `account`. 69 | */ 70 | function balanceOf(address account) external view returns (uint256); 71 | 72 | /** 73 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 74 | * 75 | * Returns a boolean value indicating whether the operation succeeded. 76 | * 77 | * Emits a {Transfer} event. 78 | */ 79 | function transfer(address recipient, uint256 amount) external returns (bool); 80 | 81 | /** 82 | * @dev Returns the remaining number of tokens that `spender` will be 83 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 84 | * zero by default. 85 | * 86 | * This value changes when {approve} or {transferFrom} are called. 87 | */ 88 | function allowance(address owner, address spender) external view returns (uint256); 89 | 90 | /** 91 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 92 | * 93 | * Returns a boolean value indicating whether the operation succeeded. 94 | * 95 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 96 | * that someone may use both the old and the new allowance by unfortunate 97 | * transaction ordering. One possible solution to mitigate this race 98 | * condition is to first reduce the spender's allowance to 0 and set the 99 | * desired value afterwards: 100 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 101 | * 102 | * Emits an {Approval} event. 103 | */ 104 | function approve(address spender, uint256 amount) external returns (bool); 105 | 106 | /** 107 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 108 | * allowance mechanism. `amount` is then deducted from the caller's 109 | * allowance. 110 | * 111 | * Returns a boolean value indicating whether the operation succeeded. 112 | * 113 | * Emits a {Transfer} event. 114 | */ 115 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 116 | 117 | /** 118 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 119 | * another (`to`). 120 | * 121 | * Note that `value` may be zero. 122 | */ 123 | event Transfer(address indexed from, address indexed to, uint256 value); 124 | 125 | /** 126 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 127 | * a call to {approve}. `value` is the new allowance. 128 | */ 129 | event Approval(address indexed owner, address indexed spender, uint256 value); 130 | } 131 | 132 | library LowGasSafeMath { 133 | /// @notice Returns x + y, reverts if sum overflows uint256 134 | /// @param x The augend 135 | /// @param y The addend 136 | /// @return z The sum of x and y 137 | function add(uint256 x, uint256 y) internal pure returns (uint256 z) { 138 | require((z = x + y) >= x); 139 | } 140 | 141 | /// @notice Returns x - y, reverts if underflows 142 | /// @param x The minuend 143 | /// @param y The subtrahend 144 | /// @return z The difference of x and y 145 | function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { 146 | require((z = x - y) <= x); 147 | } 148 | 149 | /// @notice Returns x * y, reverts if overflows 150 | /// @param x The multiplicand 151 | /// @param y The multiplier 152 | /// @return z The product of x and y 153 | function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { 154 | require(x == 0 || (z = x * y) / x == y); 155 | } 156 | 157 | /// @notice Returns x + y, reverts if overflows or underflows 158 | /// @param x The augend 159 | /// @param y The addend 160 | /// @return z The sum of x and y 161 | function add(int256 x, int256 y) internal pure returns (int256 z) { 162 | require((z = x + y) >= x == (y >= 0)); 163 | } 164 | 165 | /// @notice Returns x - y, reverts if overflows or underflows 166 | /// @param x The minuend 167 | /// @param y The subtrahend 168 | /// @return z The difference of x and y 169 | function sub(int256 x, int256 y) internal pure returns (int256 z) { 170 | require((z = x - y) <= x == (y >= 0)); 171 | } 172 | 173 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 174 | return div(a, b, "SafeMath: division by zero"); 175 | } 176 | 177 | /** 178 | * @dev Returns the integer division of two unsigned integers. Reverts with custom message on 179 | * division by zero. The result is rounded towards zero. 180 | * 181 | * Counterpart to Solidity's `/` operator. Note: this function uses a 182 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 183 | * uses an invalid opcode to revert (consuming all remaining gas). 184 | * 185 | * Requirements: 186 | * 187 | * - The divisor cannot be zero. 188 | */ 189 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 190 | require(b > 0, errorMessage); 191 | uint256 c = a / b; 192 | assert(a == b * c + a % b); // There is no case in which this doesn't hold 193 | 194 | return c; 195 | } 196 | } 197 | 198 | abstract contract ERC20 is IERC20, Initializable { 199 | 200 | using LowGasSafeMath for uint256; 201 | 202 | // Present in ERC777 203 | mapping (address => uint256) internal _balances; 204 | 205 | // Present in ERC777 206 | mapping (address => mapping (address => uint256)) internal _allowances; 207 | 208 | // Present in ERC777 209 | uint256 internal _totalSupply; 210 | 211 | // Present in ERC777 212 | string internal _name; 213 | 214 | // Present in ERC777 215 | string internal _symbol; 216 | 217 | // Present in ERC777 218 | uint8 internal _decimals; 219 | 220 | function __ERC20_init_unchained(string memory name_, string memory symbol_, uint8 decimals_) internal initializer { 221 | _name = name_; 222 | _symbol = symbol_; 223 | _decimals = decimals_; 224 | } 225 | 226 | function name() public view returns (string memory) { 227 | return _name; 228 | } 229 | 230 | function symbol() public view returns (string memory) { 231 | return _symbol; 232 | } 233 | 234 | function decimals() public view returns (uint8) { 235 | return _decimals; 236 | } 237 | 238 | function totalSupply() public view override returns (uint256) { 239 | return _totalSupply; 240 | } 241 | 242 | function balanceOf(address account) public view virtual override returns (uint256) { 243 | return _balances[account]; 244 | } 245 | 246 | function transfer(address recipient, uint256 amount) public virtual override returns (bool) { 247 | _transfer(msg.sender, recipient, amount); 248 | return true; 249 | } 250 | 251 | function allowance(address owner, address spender) public view virtual override returns (uint256) { 252 | return _allowances[owner][spender]; 253 | } 254 | 255 | function approve(address spender, uint256 amount) public virtual override returns (bool) { 256 | _approve(msg.sender, spender, amount); 257 | return true; 258 | } 259 | 260 | function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { 261 | _transfer(sender, recipient, amount); 262 | _approve(sender, msg.sender, _allowances[sender][msg.sender] 263 | .sub(amount)); 264 | return true; 265 | } 266 | 267 | function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { 268 | _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue)); 269 | return true; 270 | } 271 | 272 | function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { 273 | _approve(msg.sender, spender, _allowances[msg.sender][spender] 274 | .sub(subtractedValue)); 275 | return true; 276 | } 277 | 278 | function _transfer(address sender, address recipient, uint256 amount) internal virtual { 279 | require(sender != address(0), "ERC20: transfer from the zero address"); 280 | require(recipient != address(0), "ERC20: transfer to the zero address"); 281 | 282 | _beforeTokenTransfer(sender, recipient, amount); 283 | 284 | _balances[sender] = _balances[sender].sub(amount); 285 | _balances[recipient] = _balances[recipient].add(amount); 286 | emit Transfer(sender, recipient, amount); 287 | } 288 | 289 | function _mint(address account_, uint256 amount_) internal virtual { 290 | require(account_ != address(0), "ERC20: mint to the zero address"); 291 | _beforeTokenTransfer(address( this ), account_, amount_); 292 | _totalSupply = _totalSupply.add(amount_); 293 | _balances[account_] = _balances[account_].add(amount_); 294 | emit Transfer(address(0), account_, amount_); 295 | } 296 | 297 | function _burn(address account, uint256 amount) internal virtual { 298 | require(account != address(0), "ERC20: burn from the zero address"); 299 | 300 | _beforeTokenTransfer(account, address(0), amount); 301 | 302 | _balances[account] = _balances[account].sub(amount); 303 | _totalSupply = _totalSupply.sub(amount); 304 | emit Transfer(account, address(0), amount); 305 | } 306 | 307 | function _approve(address owner, address spender, uint256 amount) internal virtual { 308 | require(owner != address(0), "ERC20: approve from the zero address"); 309 | require(spender != address(0), "ERC20: approve to the zero address"); 310 | 311 | _allowances[owner][spender] = amount; 312 | emit Approval(owner, spender, amount); 313 | } 314 | 315 | function _beforeTokenTransfer( address from_, address to_, uint256 amount_ ) internal virtual { } 316 | } 317 | 318 | /** 319 | * @dev Contract module which allows children to implement an emergency stop 320 | * mechanism that can be triggered by an authorized account. 321 | * 322 | * This module is used through inheritance. It will make available the 323 | * modifiers `whenNotPaused` and `whenPaused`, which can be applied to 324 | * the functions of your contract. Note that they will not be pausable by 325 | * simply including this module, only once the modifiers are put in place. 326 | */ 327 | abstract contract Pausable { 328 | /** 329 | * @dev Emitted when the pause is triggered by `account`. 330 | */ 331 | event Paused(address account); 332 | 333 | /** 334 | * @dev Emitted when the pause is lifted by `account`. 335 | */ 336 | event Unpaused(address account); 337 | 338 | bool private _paused; 339 | 340 | /** 341 | * @dev Initializes the contract in unpaused state. 342 | */ 343 | constructor() { 344 | _paused = false; 345 | } 346 | 347 | /** 348 | * @dev Returns true if the contract is paused, and false otherwise. 349 | */ 350 | function paused() public view virtual returns (bool) { 351 | return _paused; 352 | } 353 | 354 | /** 355 | * @dev Modifier to make a function callable only when the contract is not paused. 356 | * 357 | * Requirements: 358 | * 359 | * - The contract must not be paused. 360 | */ 361 | modifier whenNotPaused() { 362 | require(!paused(), "Pausable: paused"); 363 | _; 364 | } 365 | 366 | /** 367 | * @dev Modifier to make a function callable only when the contract is paused. 368 | * 369 | * Requirements: 370 | * 371 | * - The contract must be paused. 372 | */ 373 | modifier whenPaused() { 374 | require(paused(), "Pausable: not paused"); 375 | _; 376 | } 377 | 378 | /** 379 | * @dev Triggers stopped state. 380 | * 381 | * Requirements: 382 | * 383 | * - The contract must not be paused. 384 | */ 385 | function _pause() internal virtual whenNotPaused { 386 | _paused = true; 387 | emit Paused(msg.sender); 388 | } 389 | 390 | /** 391 | * @dev Returns to normal state. 392 | * 393 | * Requirements: 394 | * 395 | * - The contract must be paused. 396 | */ 397 | function _unpause() internal virtual whenPaused { 398 | _paused = false; 399 | emit Unpaused(msg.sender); 400 | } 401 | } 402 | 403 | interface IOwnable { 404 | function owner() external view returns (address); 405 | 406 | function renounceOwnership() external; 407 | 408 | function transferOwnership( address newOwner_ ) external; 409 | } 410 | 411 | contract Ownable is IOwnable, Initializable { 412 | 413 | address internal _owner; 414 | address internal _pendingOwner; 415 | 416 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 417 | event OwnershipTransferring(address indexed owner, address indexed pendingOwner); 418 | 419 | function __Ownable_init_unchain() internal initializer { 420 | require(_owner == address(0)); 421 | _owner = msg.sender; 422 | emit OwnershipTransferred( address(0), _owner ); 423 | } 424 | 425 | function owner() public view override returns (address) { 426 | return _owner; 427 | } 428 | 429 | modifier onlyOwner() { 430 | require( _owner == msg.sender, "Ownable: caller is not the owner" ); 431 | _; 432 | } 433 | 434 | function renounceOwnership() public virtual override onlyOwner() { 435 | emit OwnershipTransferred( _owner, address(0) ); 436 | _owner = address(0); 437 | } 438 | 439 | function transferOwnership( address newOwner_ ) public virtual override onlyOwner() { 440 | require( newOwner_ != address(0), "Ownable: new owner is the zero address"); 441 | emit OwnershipTransferring( _owner, newOwner_ ); 442 | _pendingOwner = newOwner_; 443 | } 444 | 445 | function acceptOwnership() external { 446 | require(_pendingOwner == msg.sender, "Permission denied"); 447 | emit OwnershipTransferred( _owner, msg.sender ); 448 | _owner = msg.sender; 449 | } 450 | } 451 | 452 | contract VaultOwned is Ownable { 453 | 454 | function __VaultOwned_init_unchain() internal initializer { 455 | __Ownable_init_unchain(); 456 | } 457 | 458 | // address internal _vault; 459 | mapping(address => bool) _vault; 460 | 461 | event VaultTransferred(address indexed newVault); 462 | 463 | function setVault( address vault_ ) external onlyOwner() { 464 | require(vault_ != address(0), "IA0"); 465 | _vault[vault_] = true; 466 | emit VaultTransferred( vault_ ); 467 | } 468 | 469 | function vault(address account_) public view returns (bool) { 470 | return _vault[account_]; 471 | } 472 | 473 | modifier onlyVault() { 474 | require( _vault[msg.sender], "VaultOwned: caller is not the Vault" ); 475 | _; 476 | } 477 | 478 | } 479 | 480 | interface ISATTimelock { 481 | function increaseReward() external; 482 | } 483 | 484 | contract MockSATERC20Token is ERC20, VaultOwned, Pausable { 485 | 486 | using LowGasSafeMath for uint256; 487 | 488 | uint256 public constant CAP = 10**8 * 1 ether; 489 | uint256 public constant RATIO_FEE = 0.1 ether; 490 | 491 | address public lockedAddress; 492 | mapping(address => bool) public isTransferWhitelist; 493 | 494 | function __SATERC20Token_initialize() external initializer { 495 | __ERC20_init_unchained("Test Synassets Token", "TestSAT", 18); 496 | __VaultOwned_init_unchain(); 497 | } 498 | 499 | function mint(address account_, uint256 amount_) external onlyVault() { 500 | require(totalSupply().add(amount_) <= CAP, 'CE'); 501 | _mint(account_, amount_); 502 | 503 | if (lockedAddress != address(0)) { 504 | uint256 fee_ = amount_.mul(RATIO_FEE).div(1 ether); 505 | require(totalSupply().add(fee_) <= CAP, 'CE'); 506 | 507 | _mint(lockedAddress, fee_); 508 | ISATTimelock(lockedAddress).increaseReward(); 509 | } 510 | } 511 | 512 | function burn(uint256 amount_) external virtual { 513 | _burn(msg.sender, amount_); 514 | } 515 | 516 | function burnFrom(address account_, uint256 amount_) external virtual { 517 | _burnFrom(account_, amount_); 518 | } 519 | 520 | function _burnFrom(address account_, uint256 amount_) internal virtual { 521 | uint256 decreasedAllowance_ = allowance(account_, msg.sender).sub(amount_); 522 | 523 | _approve(account_, msg.sender, decreasedAllowance_); 524 | _burn(account_, amount_); 525 | } 526 | 527 | function _beforeTokenTransfer( 528 | address from, 529 | address to, 530 | uint256 amount 531 | ) internal virtual override { 532 | super._beforeTokenTransfer(from, to, amount); 533 | 534 | require(!paused() || isTransferWhitelist[from], "ERC20Pausable: token transfer while paused"); 535 | } 536 | 537 | function addWhitelist(address account_) external onlyOwner { 538 | isTransferWhitelist[account_] = true; 539 | } 540 | 541 | function removeWhitelist(address account_) external onlyOwner { 542 | delete isTransferWhitelist[account_]; 543 | } 544 | 545 | function unpauseTransfer() external onlyOwner { 546 | _unpause(); 547 | } 548 | 549 | function pauseTransfer() external onlyOwner { 550 | _pause(); 551 | } 552 | 553 | function setLockedAddress(address lockedAddress_) external onlyOwner { 554 | lockedAddress = lockedAddress_; 555 | } 556 | 557 | function maxSupply() public pure returns (uint256) { 558 | return CAP; 559 | } 560 | } 561 | -------------------------------------------------------------------------------- /contracts/mock/MockUDSC.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity 0.7.5; 3 | 4 | /** 5 | * @title Initializable 6 | * 7 | * @dev Helper contract to support initializer functions. To use it, replace 8 | * the constructor with a function that has the `initializer` modifier. 9 | * WARNING: Unlike constructors, initializer functions must be manually 10 | * invoked. This applies both to deploying an Initializable contract, as well 11 | * as extending an Initializable contract via inheritance. 12 | * WARNING: When used with inheritance, manual care must be taken to not invoke 13 | * a parent initializer twice, or ensure that all initializers are idempotent, 14 | * because this is not dealt with automatically as with constructors. 15 | */ 16 | contract Initializable { 17 | 18 | /** 19 | * @dev Indicates that the contract has been initialized. 20 | */ 21 | bool private initialized; 22 | 23 | /** 24 | * @dev Indicates that the contract is in the process of being initialized. 25 | */ 26 | bool private initializing; 27 | 28 | /** 29 | * @dev Modifier to use in the initializer function of a contract. 30 | */ 31 | modifier initializer() { 32 | require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized"); 33 | 34 | bool isTopLevelCall = !initializing; 35 | if (isTopLevelCall) { 36 | initializing = true; 37 | initialized = true; 38 | } 39 | 40 | _; 41 | 42 | if (isTopLevelCall) { 43 | initializing = false; 44 | } 45 | } 46 | 47 | /// @dev Returns true if and only if the function is running in the constructor 48 | function isConstructor() private view returns (bool) { 49 | // extcodesize checks the size of the code stored in an address, and 50 | // address returns the current address. Since the code is still not 51 | // deployed when running a constructor, any checks on its code size will 52 | // yield zero, making it an effective way to detect if a contract is 53 | // under construction or not. 54 | address self = address(this); 55 | uint256 cs; 56 | assembly { cs := extcodesize(self) } 57 | return cs == 0; 58 | } 59 | } 60 | 61 | interface IERC20 { 62 | /** 63 | * @dev Returns the amount of tokens in existence. 64 | */ 65 | function totalSupply() external view returns (uint256); 66 | 67 | /** 68 | * @dev Returns the amount of tokens owned by `account`. 69 | */ 70 | function balanceOf(address account) external view returns (uint256); 71 | 72 | /** 73 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 74 | * 75 | * Returns a boolean value indicating whether the operation succeeded. 76 | * 77 | * Emits a {Transfer} event. 78 | */ 79 | function transfer(address recipient, uint256 amount) external returns (bool); 80 | 81 | /** 82 | * @dev Returns the remaining number of tokens that `spender` will be 83 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 84 | * zero by default. 85 | * 86 | * This value changes when {approve} or {transferFrom} are called. 87 | */ 88 | function allowance(address owner, address spender) external view returns (uint256); 89 | 90 | /** 91 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 92 | * 93 | * Returns a boolean value indicating whether the operation succeeded. 94 | * 95 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 96 | * that someone may use both the old and the new allowance by unfortunate 97 | * transaction ordering. One possible solution to mitigate this race 98 | * condition is to first reduce the spender's allowance to 0 and set the 99 | * desired value afterwards: 100 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 101 | * 102 | * Emits an {Approval} event. 103 | */ 104 | function approve(address spender, uint256 amount) external returns (bool); 105 | 106 | /** 107 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 108 | * allowance mechanism. `amount` is then deducted from the caller's 109 | * allowance. 110 | * 111 | * Returns a boolean value indicating whether the operation succeeded. 112 | * 113 | * Emits a {Transfer} event. 114 | */ 115 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 116 | 117 | /** 118 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 119 | * another (`to`). 120 | * 121 | * Note that `value` may be zero. 122 | */ 123 | event Transfer(address indexed from, address indexed to, uint256 value); 124 | 125 | /** 126 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 127 | * a call to {approve}. `value` is the new allowance. 128 | */ 129 | event Approval(address indexed owner, address indexed spender, uint256 value); 130 | } 131 | 132 | library LowGasSafeMath { 133 | /// @notice Returns x + y, reverts if sum overflows uint256 134 | /// @param x The augend 135 | /// @param y The addend 136 | /// @return z The sum of x and y 137 | function add(uint256 x, uint256 y) internal pure returns (uint256 z) { 138 | require((z = x + y) >= x); 139 | } 140 | 141 | /// @notice Returns x - y, reverts if underflows 142 | /// @param x The minuend 143 | /// @param y The subtrahend 144 | /// @return z The difference of x and y 145 | function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { 146 | require((z = x - y) <= x); 147 | } 148 | 149 | /// @notice Returns x * y, reverts if overflows 150 | /// @param x The multiplicand 151 | /// @param y The multiplier 152 | /// @return z The product of x and y 153 | function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { 154 | require(x == 0 || (z = x * y) / x == y); 155 | } 156 | 157 | /// @notice Returns x + y, reverts if overflows or underflows 158 | /// @param x The augend 159 | /// @param y The addend 160 | /// @return z The sum of x and y 161 | function add(int256 x, int256 y) internal pure returns (int256 z) { 162 | require((z = x + y) >= x == (y >= 0)); 163 | } 164 | 165 | /// @notice Returns x - y, reverts if overflows or underflows 166 | /// @param x The minuend 167 | /// @param y The subtrahend 168 | /// @return z The difference of x and y 169 | function sub(int256 x, int256 y) internal pure returns (int256 z) { 170 | require((z = x - y) <= x == (y >= 0)); 171 | } 172 | 173 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 174 | return div(a, b, "SafeMath: division by zero"); 175 | } 176 | 177 | /** 178 | * @dev Returns the integer division of two unsigned integers. Reverts with custom message on 179 | * division by zero. The result is rounded towards zero. 180 | * 181 | * Counterpart to Solidity's `/` operator. Note: this function uses a 182 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 183 | * uses an invalid opcode to revert (consuming all remaining gas). 184 | * 185 | * Requirements: 186 | * 187 | * - The divisor cannot be zero. 188 | */ 189 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 190 | require(b > 0, errorMessage); 191 | uint256 c = a / b; 192 | assert(a == b * c + a % b); // There is no case in which this doesn't hold 193 | 194 | return c; 195 | } 196 | } 197 | 198 | abstract contract ERC20 is IERC20, Initializable { 199 | 200 | using LowGasSafeMath for uint256; 201 | 202 | // Present in ERC777 203 | mapping (address => uint256) internal _balances; 204 | 205 | // Present in ERC777 206 | mapping (address => mapping (address => uint256)) internal _allowances; 207 | 208 | // Present in ERC777 209 | uint256 internal _totalSupply; 210 | 211 | // Present in ERC777 212 | string internal _name; 213 | 214 | // Present in ERC777 215 | string internal _symbol; 216 | 217 | // Present in ERC777 218 | uint8 internal _decimals; 219 | 220 | function __ERC20_init_unchained(string memory name_, string memory symbol_, uint8 decimals_) internal initializer { 221 | _name = name_; 222 | _symbol = symbol_; 223 | _decimals = decimals_; 224 | } 225 | 226 | function name() public view returns (string memory) { 227 | return _name; 228 | } 229 | 230 | function symbol() public view returns (string memory) { 231 | return _symbol; 232 | } 233 | 234 | function decimals() public view returns (uint8) { 235 | return _decimals; 236 | } 237 | 238 | function totalSupply() public view override returns (uint256) { 239 | return _totalSupply; 240 | } 241 | 242 | function balanceOf(address account) public view virtual override returns (uint256) { 243 | return _balances[account]; 244 | } 245 | 246 | function transfer(address recipient, uint256 amount) public virtual override returns (bool) { 247 | _transfer(msg.sender, recipient, amount); 248 | return true; 249 | } 250 | 251 | function allowance(address owner, address spender) public view virtual override returns (uint256) { 252 | return _allowances[owner][spender]; 253 | } 254 | 255 | function approve(address spender, uint256 amount) public virtual override returns (bool) { 256 | _approve(msg.sender, spender, amount); 257 | return true; 258 | } 259 | 260 | function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { 261 | _transfer(sender, recipient, amount); 262 | _approve(sender, msg.sender, _allowances[sender][msg.sender] 263 | .sub(amount)); 264 | return true; 265 | } 266 | 267 | function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { 268 | _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue)); 269 | return true; 270 | } 271 | 272 | function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { 273 | _approve(msg.sender, spender, _allowances[msg.sender][spender] 274 | .sub(subtractedValue)); 275 | return true; 276 | } 277 | 278 | function _transfer(address sender, address recipient, uint256 amount) internal virtual { 279 | require(sender != address(0), "ERC20: transfer from the zero address"); 280 | require(recipient != address(0), "ERC20: transfer to the zero address"); 281 | 282 | _beforeTokenTransfer(sender, recipient, amount); 283 | 284 | _balances[sender] = _balances[sender].sub(amount); 285 | _balances[recipient] = _balances[recipient].add(amount); 286 | emit Transfer(sender, recipient, amount); 287 | } 288 | 289 | function _mint(address account_, uint256 amount_) internal virtual { 290 | require(account_ != address(0), "ERC20: mint to the zero address"); 291 | _beforeTokenTransfer(address( this ), account_, amount_); 292 | _totalSupply = _totalSupply.add(amount_); 293 | _balances[account_] = _balances[account_].add(amount_); 294 | emit Transfer(address(0), account_, amount_); 295 | } 296 | 297 | function _burn(address account, uint256 amount) internal virtual { 298 | require(account != address(0), "ERC20: burn from the zero address"); 299 | 300 | _beforeTokenTransfer(account, address(0), amount); 301 | 302 | _balances[account] = _balances[account].sub(amount); 303 | _totalSupply = _totalSupply.sub(amount); 304 | emit Transfer(account, address(0), amount); 305 | } 306 | 307 | function _approve(address owner, address spender, uint256 amount) internal virtual { 308 | require(owner != address(0), "ERC20: approve from the zero address"); 309 | require(spender != address(0), "ERC20: approve to the zero address"); 310 | 311 | _allowances[owner][spender] = amount; 312 | emit Approval(owner, spender, amount); 313 | } 314 | 315 | function _beforeTokenTransfer( address from_, address to_, uint256 amount_ ) internal virtual { } 316 | } 317 | 318 | contract MockUSDC is ERC20 { 319 | 320 | function __MockUSDC_initialize() external initializer { 321 | __ERC20_init_unchained('TEST USDC', 'TestUSDC', 6); 322 | } 323 | 324 | // allow anyone to mint 325 | function mint() external { 326 | _mint(msg.sender, 20000 * 1e6); 327 | } 328 | 329 | } -------------------------------------------------------------------------------- /docs/how_it_all_works.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synassets/contracts/d4efb39761ae6c6e831fec2609467de1c6c998dc/docs/how_it_all_works.jpeg -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-waffle"); 2 | 3 | module.exports = { 4 | solidity: "0.7.5", 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "synassets-contracts", 3 | "version": "1.0.0", 4 | "description": "Smart Contracts for Synassets", 5 | "directories": { 6 | "test": "test" 7 | }, 8 | "dependencies": { 9 | "@openzeppelin/contracts": "^3.4.0" 10 | }, 11 | "devDependencies": { 12 | "@nomiclabs/hardhat-ethers": "^2.0.2", 13 | "@nomiclabs/hardhat-waffle": "^2.0.1", 14 | "@openzeppelin/test-helpers": "^0.5.12", 15 | "chai": "^4.3.4", 16 | "ethereum-waffle": "^3.4.0", 17 | "ethers": "^5.4.1", 18 | "hardhat": "^2.5.0", 19 | "prettier": "^2.3.2", 20 | "prettier-plugin-solidity": "^1.0.0-beta.17", 21 | "solhint": "^3.3.6", 22 | "solhint-plugin-prettier": "0.0.5" 23 | }, 24 | "scripts": { 25 | "test": "npx hardhat test", 26 | "compile": "npx hardhat compile", 27 | "clean": "npx hardhat clean", 28 | "start": "npx hardhat run scripts/deployAll.js", 29 | "lint:sol": "solhint -f table contracts/**/*.sol" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /scripts/ExportTokenSaleData.js: -------------------------------------------------------------------------------- 1 | // We require the Hardhat Runtime Environment explicitly here. This is optional 2 | // but useful for running the script in a standalone fashion through `node