├── .prettierrc ├── deployments ├── bsc │ └── .chainId ├── mumbai │ └── .chainId ├── polygon │ └── .chainId └── bsc_testnet │ └── .chainId ├── .solhintignore ├── .npmignore ├── .eslintignore ├── .DS_Store ├── commitlint.config.js ├── .prettierignore ├── .husky └── commit-msg ├── .env.example ├── .solhint.json ├── contracts ├── connectors │ ├── UniswapV2Factory.sol │ ├── UniswapV2Router02.sol │ ├── interfaces │ │ └── IExchangeConnector.sol │ └── UniswapV2Connector.sol ├── erc20 │ ├── erc20.sol │ ├── TeleBTCProxy.sol │ ├── interfaces │ │ ├── IWETH.sol │ │ └── ITeleBTC.sol │ ├── WETH.sol │ ├── TokenBatchTransfer.sol │ ├── Returner.sol │ └── TeleBTCLogic.sol ├── lockers │ ├── LockersProxy.sol │ ├── interfaces │ │ ├── ILockersStorage.sol │ │ └── ILockers.sol │ └── LockersStorageStructure.sol ├── routers │ ├── BurnRouterProxy.sol │ ├── CcExchangeRouterProxy.sol │ ├── CcTransferRouterProxy.sol │ ├── CcTransferRouterStorage.sol │ ├── CcExchangeRouterStorage.sol │ ├── interfaces │ │ ├── IBurnRouterStorage.sol │ │ ├── ICcTransferRouterStorage.sol │ │ ├── ICcExchangeRouterStorage.sol │ │ ├── ICcTransferRouter.sol │ │ ├── ICcExchangeRouter.sol │ │ └── IBurnRouter.sol │ └── BurnRouterStorage.sol ├── types │ └── DataTypes.sol ├── oracle │ └── interfaces │ │ └── IPriceOracle.sol └── libraries │ ├── RequestHelper.sol │ ├── BurnRouterLib.sol │ └── LockersLib.sol ├── .gitignore ├── tsconfig.json ├── test ├── block_utils.js ├── block_utils.ts ├── utils.js ├── test_fixtures │ ├── ccBurnRequests.json │ ├── ccTransferRequests.json │ └── ccExchangeRequests.json └── telebtc.test.ts ├── .eslintrc.js ├── helper-functions.ts ├── deploy ├── 004_TeleBTCLogic.ts ├── 015_CcExchangeRouterLogic.ts ├── 013_CcTransferRouterLogic.ts ├── 011_LockersLogic.ts ├── 006_TokenBatchTransfer.ts ├── 010_PriceOracle.ts ├── 007_Returner.ts ├── 009_ExchangeConnector.ts ├── 005_TeleBTCProxy.ts ├── 012_LockersProxy.ts ├── 014_BurnRouterProxy.ts ├── 013_CcTransferRouterProxy.ts ├── 015_CcExchangeRouterProx.ts └── 014_BurnRouterLogic.ts ├── scripts ├── update-contracts │ ├── 004_SetPriceOracle.ts │ ├── 001_SetRelay.ts │ └── 003_SetTeleBTC.ts └── init-config │ ├── 004-add-liquidity-for-testnet.ts │ ├── 001-init-contracts.ts │ └── 002-set-params.ts ├── .solcover.js ├── config ├── mumbai.json ├── bsc_testnet.json ├── bsc.json └── polygon.json ├── README.md ├── package.json └── hardhat.config.ts /.prettierrc: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /deployments/bsc/.chainId: -------------------------------------------------------------------------------- 1 | 56 -------------------------------------------------------------------------------- /.solhintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /deployments/mumbai/.chainId: -------------------------------------------------------------------------------- 1 | 80001 -------------------------------------------------------------------------------- /deployments/polygon/.chainId: -------------------------------------------------------------------------------- 1 | 137 -------------------------------------------------------------------------------- /deployments/bsc_testnet/.chainId: -------------------------------------------------------------------------------- 1 | 97 -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | hardhat.config.ts 2 | scripts 3 | test 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | artifacts 3 | cache 4 | coverage 5 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeleportDAO/teleswap-contracts-evm/HEAD/.DS_Store -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {extends: ['@commitlint/config-conventional']} 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | artifacts 3 | cache 4 | coverage* 5 | gasReporterOutput.json 6 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx --no -- commitlint --edit 5 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | ETHERSCAN_API_KEY=ABC123ABC123ABC123ABC123ABC123ABC1 2 | PRIVATE_KEY=0xabc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1 3 | PROJECT_ID= 4 | VERIFY_OPTION=0 -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "compiler-version": ["error", "^0.8.0"], 5 | "func-visibility": ["warn", { "ignoreConstructors": true }] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /contracts/connectors/UniswapV2Factory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity <0.8.4; 3 | 4 | import "@uniswap/v2-core/contracts/UniswapV2Factory.sol"; 5 | 6 | contract UniswapV2 is UniswapV2Factory { 7 | 8 | constructor( 9 | address _feeToSetter 10 | ) UniswapV2Factory(_feeToSetter) public { 11 | // JUST FOR TEST 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain 6 | 7 | #Hardhat files 8 | cache 9 | artifacts 10 | 11 | 12 | contracts.json 13 | coverage* 14 | deployments/localhost 15 | deployments/*/solcInputs/*.json 16 | dist 17 | src/types 18 | .vscode 19 | .nodn 20 | *.abe* 21 | *.bii 22 | /out 23 | build 24 | .env 25 | .venv 26 | /export 27 | vendor 28 | 29 | yarn-error.log 30 | -------------------------------------------------------------------------------- /contracts/connectors/UniswapV2Router02.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity <0.8.4; 3 | 4 | import "@uniswap/v2-periphery/contracts/UniswapV2Router02.sol"; 5 | 6 | contract UniswapV2 is UniswapV2Router02 { 7 | 8 | constructor( 9 | address _factory, 10 | address _WETH 11 | ) UniswapV2Router02(_factory, _WETH) public { 12 | // JUST FOR TEST 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "commonjs", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "outDir": "dist", 8 | "declaration": true 9 | }, 10 | "include": [ 11 | "hardhat.config.ts", 12 | "./scripts", 13 | "./deploy", 14 | "./test", 15 | "./typechain" 16 | ], 17 | "files": ["./hardhat.config.ts"] 18 | } 19 | -------------------------------------------------------------------------------- /contracts/erc20/erc20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.8.0 <0.8.4; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | 7 | contract erc20 is ERC20 { 8 | constructor ( 9 | string memory name_, 10 | string memory symbol_, 11 | uint initialMintedAmount 12 | ) ERC20(name_, symbol_) { 13 | _mint(_msgSender(), initialMintedAmount); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /contracts/erc20/TeleBTCProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; 5 | 6 | contract TeleBTCProxy is TransparentUpgradeableProxy { 7 | 8 | constructor( 9 | address _logic, 10 | address admin_, 11 | bytes memory _data 12 | ) payable TransparentUpgradeableProxy(_logic, admin_, _data) {} 13 | 14 | } -------------------------------------------------------------------------------- /contracts/lockers/LockersProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; 5 | 6 | contract LockersProxy is TransparentUpgradeableProxy { 7 | 8 | constructor( 9 | address _logic, 10 | address admin_, 11 | bytes memory _data 12 | ) payable TransparentUpgradeableProxy(_logic, admin_, _data) {} 13 | 14 | } -------------------------------------------------------------------------------- /contracts/routers/BurnRouterProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; 5 | 6 | contract BurnRouterProxy is TransparentUpgradeableProxy { 7 | 8 | constructor( 9 | address _logic, 10 | address _admin, 11 | bytes memory _data 12 | ) payable TransparentUpgradeableProxy(_logic, _admin, _data) {} 13 | 14 | } -------------------------------------------------------------------------------- /contracts/routers/CcExchangeRouterProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; 5 | 6 | contract CcExchangeRouterProxy is TransparentUpgradeableProxy { 7 | 8 | constructor( 9 | address _logic, 10 | address _admin, 11 | bytes memory _data 12 | ) payable TransparentUpgradeableProxy(_logic, _admin, _data) {} 13 | 14 | } -------------------------------------------------------------------------------- /contracts/routers/CcTransferRouterProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; 5 | 6 | contract CcTransferRouterProxy is TransparentUpgradeableProxy { 7 | 8 | constructor( 9 | address _logic, 10 | address _admin, 11 | bytes memory _data 12 | ) payable TransparentUpgradeableProxy(_logic, _admin, _data) {} 13 | 14 | } -------------------------------------------------------------------------------- /contracts/erc20/interfaces/IWETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IWETH is IERC20 { 7 | 8 | function name() external view returns (string memory); 9 | function symbol() external view returns (string memory); 10 | function decimals() external view returns (uint8); 11 | 12 | function deposit() external payable; 13 | function withdraw(uint) external; 14 | } -------------------------------------------------------------------------------- /test/block_utils.js: -------------------------------------------------------------------------------- 1 | 2 | const advanceBlockWithTime = async (provider, seconds) => { 3 | await provider.send("evm_increaseTime", [seconds]) 4 | await provider.send("evm_mine") 5 | } 6 | 7 | const takeSnapshot = async (provider) => { 8 | return await provider.send("evm_snapshot") 9 | } 10 | 11 | const revertProvider = async (provider, snapshotId) => { 12 | await provider.send("evm_revert", [snapshotId]) 13 | } 14 | 15 | module.exports = { 16 | advanceBlockWithTime, 17 | takeSnapshot, 18 | revertProvider, 19 | } 20 | -------------------------------------------------------------------------------- /test/block_utils.ts: -------------------------------------------------------------------------------- 1 | const advanceBlockWithTime = async (provider: any, seconds: number) => { 2 | await provider.send("evm_increaseTime", [seconds]) 3 | await provider.send("evm_mine") 4 | } 5 | 6 | const takeSnapshot = async (provider: any) => { 7 | return await provider.send("evm_snapshot") 8 | } 9 | 10 | const revertProvider = async (provider: any, snapshotId: any) => { 11 | await provider.send("evm_revert", [snapshotId]) 12 | } 13 | 14 | export { 15 | advanceBlockWithTime, 16 | takeSnapshot, 17 | revertProvider, 18 | } -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: false, 4 | es2021: true, 5 | mocha: true, 6 | node: true, 7 | }, 8 | plugins: ["@typescript-eslint"], 9 | extends: [ 10 | "standard", 11 | "plugin:prettier/recommended", 12 | "plugin:node/recommended", 13 | ], 14 | parser: "@typescript-eslint/parser", 15 | parserOptions: { 16 | ecmaVersion: 12, 17 | }, 18 | rules: { 19 | "node/no-unsupported-features/es-syntax": [ 20 | "error", 21 | { ignores: ["modules"] }, 22 | ], 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /helper-functions.ts: -------------------------------------------------------------------------------- 1 | import { run } from "hardhat" 2 | 3 | const verify = async (contractAddress: string, args: any[], codePath: string) => { 4 | console.log("Verifying contract...") 5 | try { 6 | await run("verify:verify", { 7 | address: contractAddress, 8 | constructorArguments: args, 9 | contract: codePath, 10 | }) 11 | } catch (e: any) { 12 | if (e.message.toLowerCase().includes("already verified")) { 13 | console.log("Already verified!") 14 | } else { 15 | console.log(e) 16 | } 17 | } 18 | } 19 | 20 | export default verify -------------------------------------------------------------------------------- /test/utils.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | strip0xPrefix: function strip0xPrefix(hexString) { 5 | return hexString.substring(0, 2) === '0x' ? hexString.substring(2) : hexString; 6 | }, 7 | 8 | concatenateHexStrings: function concatenateHexStrings(strs) { 9 | let current = '0x'; 10 | for (let i = 0; i < strs.length; i += 1) { 11 | current = `${current}${this.strip0xPrefix(strs[i])}`; 12 | } 13 | return current; 14 | }, 15 | concatenateHeadersHexes: function concatenateHeadersHexes(arr) { 16 | const hexes = arr.map(_arr => _arr.hex); 17 | return this.concatenateHexStrings(hexes); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /contracts/erc20/WETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "./interfaces/IWETH.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | 7 | contract WETH is ERC20 { 8 | 9 | constructor(string memory _name, string memory _symbol) 10 | ERC20(_name, _symbol) {} 11 | 12 | function deposit() external payable { 13 | require(msg.value > 0); 14 | _mint(_msgSender(), msg.value); 15 | } 16 | 17 | function withdraw(uint value) external { 18 | require(balanceOf(_msgSender()) >= value, "Balance is not sufficient"); 19 | _burn(_msgSender(), value); 20 | address payable recipient = payable(_msgSender()); 21 | recipient.transfer(value); 22 | } 23 | } -------------------------------------------------------------------------------- /contracts/routers/CcTransferRouterStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "./interfaces/ICcTransferRouterStorage.sol"; 5 | 6 | contract CcTransferRouterStorage is ICcTransferRouterStorage { 7 | 8 | // Constants 9 | uint constant MAX_PROTOCOL_FEE = 10000; 10 | 11 | // Public variables 12 | uint public override startingBlockNumber; 13 | uint public override chainId; 14 | uint public override appId; 15 | uint public override protocolPercentageFee; // A number between 0 to 10000 16 | address public override relay; 17 | address public override lockers; 18 | address public override teleBTC; 19 | address public override instantRouter; 20 | address public override treasury; 21 | mapping(bytes32 => ccTransferRequest) public ccTransferRequests; // TxId to ccTransferRequest structure 22 | 23 | } -------------------------------------------------------------------------------- /deploy/004_TeleBTCLogic.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { DeployFunction } from 'hardhat-deploy/types'; 3 | import verify from "../helper-functions" 4 | 5 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 6 | const { deployments, getNamedAccounts, network } = hre; 7 | const { deploy } = deployments; 8 | const { deployer } = await getNamedAccounts(); 9 | 10 | 11 | const deployedContract = await deploy("TeleBTCLogic", { 12 | from: deployer, 13 | log: true, 14 | skipIfAlreadyDeployed: true, 15 | }); 16 | 17 | if (network.name != "hardhat" && process.env.ETHERSCAN_API_KEY && process.env.VERIFY_OPTION == "1") { 18 | await verify(deployedContract.address, [], "contracts/erc20/TeleBTCLogic.sol:TeleBTCLogic") 19 | } 20 | 21 | }; 22 | 23 | export default func; 24 | func.tags = ["TeleBTCLogic"]; 25 | -------------------------------------------------------------------------------- /contracts/routers/CcExchangeRouterStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "./interfaces/ICcExchangeRouterStorage.sol"; 5 | 6 | contract CcExchangeRouterStorage is ICcExchangeRouterStorage { 7 | 8 | // Constants 9 | uint constant MAX_PROTOCOL_FEE = 10000; 10 | 11 | // Public variables 12 | uint public override startingBlockNumber; 13 | uint public override chainId; 14 | uint public override protocolPercentageFee; // A number between 0 to 10000 15 | address public override relay; 16 | address public override instantRouter; 17 | address public override lockers; 18 | address public override teleBTC; 19 | address public override treasury; 20 | mapping(uint => address) public override exchangeConnector; // mapping from app id to exchange connector address 21 | 22 | // Private variables 23 | mapping(bytes32 => ccExchangeRequest) internal ccExchangeRequests; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /contracts/routers/interfaces/IBurnRouterStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "@teleportdao/btc-evm-bridge/contracts/types/ScriptTypesEnum.sol"; 5 | 6 | interface IBurnRouterStorage { 7 | 8 | // Read-only functions 9 | 10 | function startingBlockNumber() external view returns (uint); 11 | 12 | function relay() external view returns (address); 13 | 14 | function lockers() external view returns (address); 15 | 16 | function teleBTC() external view returns (address); 17 | 18 | function treasury() external view returns (address); 19 | 20 | function transferDeadline() external view returns (uint); 21 | 22 | function protocolPercentageFee() external view returns (uint); 23 | 24 | function slasherPercentageReward() external view returns (uint); 25 | 26 | function bitcoinFee() external view returns (uint); // Bitcoin transaction fee 27 | 28 | function isUsedAsBurnProof(bytes32 _txId) external view returns (bool); 29 | 30 | function bitcoinFeeOracle() external view returns (address); 31 | 32 | } -------------------------------------------------------------------------------- /deploy/015_CcExchangeRouterLogic.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { DeployFunction } from 'hardhat-deploy/types'; 3 | import verify from "../helper-functions" 4 | 5 | require('dotenv').config({path:"../config/temp.env"}); 6 | 7 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 8 | const { deployments, getNamedAccounts, network } = hre; 9 | const { deploy } = deployments; 10 | const { deployer } = await getNamedAccounts(); 11 | 12 | const deployedContract = await deploy("CcExchangeRouterLogic", { 13 | from: deployer, 14 | log: true, 15 | skipIfAlreadyDeployed: true, 16 | args: [], 17 | }); 18 | 19 | if (network.name != "hardhat" && process.env.ETHERSCAN_API_KEY && process.env.VERIFY_OPTION == "1") { 20 | await verify( 21 | deployedContract.address, 22 | [], 23 | "contracts/routers/CcExchangeRouterLogic.sol:CcExchangeRouterLogic" 24 | ) 25 | } 26 | }; 27 | 28 | export default func; 29 | func.tags = ["CcExchangeRouterLogic"]; 30 | -------------------------------------------------------------------------------- /deploy/013_CcTransferRouterLogic.ts: -------------------------------------------------------------------------------- 1 | import {HardhatRuntimeEnvironment} from 'hardhat/types'; 2 | import {DeployFunction} from 'hardhat-deploy/types'; 3 | // import config from 'config' 4 | // import { BigNumber } from 'ethers'; 5 | import verify from "../helper-functions" 6 | 7 | import * as dotenv from "dotenv"; 8 | dotenv.config(); 9 | 10 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 11 | const { deployments, getNamedAccounts, network } = hre; 12 | const { deploy } = deployments; 13 | const { deployer } = await getNamedAccounts(); 14 | 15 | const deployedContract = await deploy("CcTransferRouterLogic", { 16 | from: deployer, 17 | log: true, 18 | skipIfAlreadyDeployed: true 19 | }); 20 | 21 | if (network.name != "hardhat" && process.env.ETHERSCAN_API_KEY && process.env.VERIFY_OPTION == "1") { 22 | await verify( 23 | deployedContract.address, 24 | [], 25 | "contracts/routers/CcTransferRouterLogic.sol:CcTransferRouterLogic" 26 | ) 27 | } 28 | }; 29 | 30 | export default func; 31 | func.tags = ["CcTransferRouterLogic"]; 32 | -------------------------------------------------------------------------------- /deploy/011_LockersLogic.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { DeployFunction } from 'hardhat-deploy/types'; 3 | import verify from "../helper-functions" 4 | 5 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 6 | const { deployments, getNamedAccounts, network } = hre; 7 | const { deploy } = deployments; 8 | const { deployer } = await getNamedAccounts(); 9 | 10 | const lockersLib = await deploy("LockersLib", { 11 | from: deployer, 12 | log: true, 13 | skipIfAlreadyDeployed: true, 14 | }) 15 | 16 | const deployedContract = await deploy("LockersLogic", { 17 | from: deployer, 18 | log: true, 19 | skipIfAlreadyDeployed: true, 20 | libraries: { 21 | "LockersLib": lockersLib.address 22 | }, 23 | }); 24 | 25 | if (network.name != "hardhat" && process.env.ETHERSCAN_API_KEY && process.env.VERIFY_OPTION == "1") { 26 | await verify(deployedContract.address, [], "contracts/lockers/LockersLogic.sol:LockersLogic") 27 | } 28 | }; 29 | 30 | export default func; 31 | func.tags = ["LockersLogic"]; 32 | -------------------------------------------------------------------------------- /deploy/006_TokenBatchTransfer.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { DeployFunction } from 'hardhat-deploy/types'; 3 | import verify from "../helper-functions" 4 | 5 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 6 | // const { deployments, getNamedAccounts, network } = hre; 7 | // const { deploy } = deployments; 8 | // const { deployer } = await getNamedAccounts(); 9 | 10 | // const teleBTC = await deployments.get("TeleBTCProxy") 11 | 12 | // const theArgs = [ 13 | // teleBTC.address 14 | // ] 15 | 16 | // const deployedContract = await deploy("TokenBatchTransfer", { 17 | // from: deployer, 18 | // log: true, 19 | // skipIfAlreadyDeployed: true, 20 | // args: theArgs 21 | // }); 22 | 23 | // if (network.name != "hardhat" && process.env.ETHERSCAN_API_KEY && process.env.VERIFY_OPTION == "1") { 24 | // await verify(deployedContract.address, 25 | // theArgs, 26 | // "contracts/erc20/TokenBatchTransfer.sol:TokenBatchTransfer") 27 | // } 28 | 29 | }; 30 | 31 | export default func; 32 | func.tags = ["TokenBatchTransfer"]; -------------------------------------------------------------------------------- /contracts/lockers/interfaces/ILockersStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "../../types/DataTypes.sol"; 5 | 6 | interface ILockersStorage { 7 | // Read-only functions 8 | 9 | function TeleportDAOToken() external view returns(address); 10 | 11 | function teleBTC() external view returns(address); 12 | 13 | function ccBurnRouter() external view returns(address); 14 | 15 | function exchangeConnector() external view returns(address); 16 | 17 | function priceOracle() external view returns(address); 18 | 19 | function minRequiredTDTLockedAmount() external view returns(uint); 20 | 21 | function minRequiredTNTLockedAmount() external view returns(uint); 22 | 23 | function lockerPercentageFee() external view returns(uint); 24 | 25 | function collateralRatio() external view returns(uint); 26 | 27 | function liquidationRatio() external view returns(uint); 28 | 29 | function priceWithDiscountRatio() external view returns(uint); 30 | 31 | function totalNumberOfCandidates() external view returns(uint); 32 | 33 | function totalNumberOfLockers() external view returns(uint); 34 | 35 | } 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /deploy/010_PriceOracle.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { DeployFunction } from 'hardhat-deploy/types'; 3 | import config from 'config'; 4 | import verify from "../helper-functions"; 5 | 6 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 7 | const { deployments, getNamedAccounts, network } = hre; 8 | const { deploy } = deployments; 9 | const { deployer } = await getNamedAccounts(); 10 | 11 | const acceptableDelay = config.get("acceptable_delay"); 12 | const tntToken = config.get("wrapped_native_token") 13 | 14 | const deployedContract = await deploy("PriceOracle", { 15 | from: deployer, 16 | log: true, 17 | skipIfAlreadyDeployed: true, 18 | args: [ 19 | acceptableDelay, 20 | tntToken 21 | ], 22 | }); 23 | 24 | if (network.name != "hardhat" && process.env.ETHERSCAN_API_KEY && process.env.VERIFY_OPTION == "1") { 25 | await verify(deployedContract.address, [ 26 | acceptableDelay, 27 | tntToken 28 | ], "contracts/oracle/PriceOracle.sol:PriceOracle") 29 | } 30 | }; 31 | 32 | export default func; 33 | func.tags = ["PriceOracle"]; 34 | -------------------------------------------------------------------------------- /deploy/007_Returner.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { DeployFunction } from 'hardhat-deploy/types'; 3 | import verify from "../helper-functions" 4 | 5 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 6 | // const { deployments, getNamedAccounts, network } = hre; 7 | // const { deploy } = deployments; 8 | // const { deployer } = await getNamedAccounts(); 9 | 10 | // const teleBTC = await deployments.get("TeleBTCProxy") 11 | 12 | // const theArgs = [ 13 | // ["WMATIC"], 14 | // ["0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889"], 15 | // teleBTC.address 16 | // ] 17 | 18 | // const deployedContract = await deploy("Returner", { 19 | // from: deployer, 20 | // log: true, 21 | // skipIfAlreadyDeployed: true, 22 | // args: theArgs 23 | // }); 24 | 25 | // if (network.name != "hardhat" && process.env.ETHERSCAN_API_KEY && process.env.VERIFY_OPTION == "1") { 26 | // await verify(deployedContract.address, 27 | // theArgs, 28 | // "contracts/erc20/Returner.sol:Returner") 29 | // } 30 | 31 | }; 32 | 33 | export default func; 34 | func.tags = ["TokenBatchTransfer"]; -------------------------------------------------------------------------------- /deploy/009_ExchangeConnector.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { DeployFunction } from 'hardhat-deploy/types'; 3 | import config from 'config'; 4 | import verify from "../helper-functions"; 5 | 6 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 7 | const { deployments, getNamedAccounts, network } = hre; 8 | const { deploy } = deployments; 9 | const { deployer } = await getNamedAccounts(); 10 | 11 | const connectorName = "QuickswapV2" 12 | 13 | const uniswapV2Router02 = config.get("uniswap_v2_router_02") 14 | 15 | const deployedContract = await deploy("UniswapV2Connector", { 16 | from: deployer, 17 | log: true, 18 | skipIfAlreadyDeployed: true, 19 | args: [ 20 | connectorName, 21 | uniswapV2Router02 22 | ], 23 | }); 24 | 25 | if (network.name != "hardhat" && process.env.ETHERSCAN_API_KEY && process.env.VERIFY_OPTION == "1") { 26 | await verify(deployedContract.address, [ 27 | connectorName, 28 | uniswapV2Router02 29 | ], "contracts/connectors/UniswapV2Connector.sol:UniswapV2Connector") 30 | } 31 | }; 32 | 33 | export default func; 34 | func.tags = ["UniswapV2Connector"]; 35 | -------------------------------------------------------------------------------- /deploy/005_TeleBTCProxy.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { DeployFunction } from 'hardhat-deploy/types'; 3 | import verify from "../helper-functions"; 4 | import config from 'config'; 5 | 6 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 7 | const { deployments, getNamedAccounts, network } = hre; 8 | const { deploy } = deployments; 9 | const { deployer } = await getNamedAccounts(); 10 | 11 | const proxyAdmin = config.get("proxy_admin"); 12 | const teleBTCLogic = await deployments.get("TeleBTCLogic") 13 | 14 | let theArgs = [ 15 | teleBTCLogic.address, 16 | proxyAdmin, 17 | "0x" 18 | ] 19 | 20 | const deployedContract = await deploy("TeleBTCProxy", { 21 | from: deployer, 22 | log: true, 23 | skipIfAlreadyDeployed: true, 24 | args: theArgs, 25 | }); 26 | 27 | if (network.name != "hardhat" && process.env.ETHERSCAN_API_KEY && process.env.VERIFY_OPTION == "1") { 28 | await verify( 29 | deployedContract.address, 30 | theArgs, 31 | "contracts/erc20/TeleBTCProxy.sol:TeleBTCProxy" 32 | ) 33 | } 34 | }; 35 | 36 | export default func; 37 | func.tags = ["TeleBTCProxy"]; 38 | -------------------------------------------------------------------------------- /deploy/012_LockersProxy.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { DeployFunction } from 'hardhat-deploy/types'; 3 | import verify from "../helper-functions"; 4 | import config from 'config'; 5 | 6 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 7 | const { deployments, getNamedAccounts, network } = hre; 8 | const { deploy } = deployments; 9 | const { deployer } = await getNamedAccounts(); 10 | 11 | const proxyAdmin = config.get("proxy_admin"); 12 | const lockersLogic = await deployments.get("LockersLogic") 13 | 14 | const deployedContract = await deploy("LockersProxy", { 15 | from: deployer, 16 | log: true, 17 | skipIfAlreadyDeployed: true, 18 | args: [ 19 | lockersLogic.address, 20 | proxyAdmin, 21 | "0x" 22 | ], 23 | }); 24 | 25 | if (network.name != "hardhat" && process.env.ETHERSCAN_API_KEY && process.env.VERIFY_OPTION == "1") { 26 | await verify( 27 | deployedContract.address, 28 | [ 29 | lockersLogic.address, 30 | proxyAdmin, 31 | "0x" 32 | ], 33 | "contracts/lockers/LockersProxy.sol:LockersProxy" 34 | ) 35 | } 36 | }; 37 | 38 | export default func; 39 | func.tags = ["LockersProxy"]; 40 | -------------------------------------------------------------------------------- /contracts/routers/interfaces/ICcTransferRouterStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | interface ICcTransferRouterStorage { 5 | 6 | // // Structures 7 | 8 | /// @notice Structure for recording cross-chain transfer requests 9 | /// @param inputAmount Amount of locked BTC on source chain 10 | /// @param recipientAddress Address of transfer recipient 11 | /// @param fee Amount of fee that is paid to Teleporter (tx, relayer and teleporter fees) 12 | /// @param speed Speed of the request (normal or instant) 13 | /// @param isUsed Whether the tx is used or not 14 | struct ccTransferRequest { 15 | uint inputAmount; 16 | address recipientAddress; 17 | uint fee; 18 | uint256 speed; 19 | bool isUsed; 20 | } 21 | 22 | // Read-only functions 23 | 24 | function startingBlockNumber() external view returns (uint); 25 | 26 | function protocolPercentageFee() external view returns (uint); 27 | 28 | function chainId() external view returns (uint); 29 | 30 | function appId() external view returns (uint); 31 | 32 | function relay() external view returns (address); 33 | 34 | function instantRouter() external view returns (address); 35 | 36 | function lockers() external view returns (address); 37 | 38 | function teleBTC() external view returns (address); 39 | 40 | function treasury() external view returns (address); 41 | 42 | } -------------------------------------------------------------------------------- /deploy/014_BurnRouterProxy.ts: -------------------------------------------------------------------------------- 1 | import {HardhatRuntimeEnvironment} from 'hardhat/types'; 2 | import {DeployFunction} from 'hardhat-deploy/types'; 3 | import verify from "../helper-functions"; 4 | import config from 'config'; 5 | 6 | import * as dotenv from "dotenv"; 7 | dotenv.config(); 8 | 9 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 10 | const { deployments, getNamedAccounts, network } = hre; 11 | const { deploy } = deployments; 12 | const { deployer } = await getNamedAccounts(); 13 | 14 | const proxyAdmin = config.get("proxy_admin"); 15 | const burnRouterLogic = await deployments.get("BurnRouterLogic") 16 | 17 | const deployedContract = await deploy("BurnRouterProxy", { 18 | from: deployer, 19 | log: true, 20 | skipIfAlreadyDeployed: true, 21 | args: [ 22 | burnRouterLogic.address, 23 | proxyAdmin, 24 | "0x" 25 | ], 26 | }); 27 | 28 | if (network.name != "hardhat" && process.env.ETHERSCAN_API_KEY && process.env.VERIFY_OPTION == "1") { 29 | await verify( 30 | deployedContract.address, 31 | [ 32 | burnRouterLogic.address, 33 | proxyAdmin, 34 | "0x" 35 | ], 36 | "contracts/routers/BurnRouterProxy.sol:BurnRouterProxy" 37 | ) 38 | } 39 | }; 40 | 41 | export default func; 42 | func.tags = ["BurnRouterProxy"]; 43 | -------------------------------------------------------------------------------- /scripts/update-contracts/004_SetPriceOracle.ts: -------------------------------------------------------------------------------- 1 | import {HardhatRuntimeEnvironment} from 'hardhat/types'; 2 | import {DeployFunction} from 'hardhat-deploy/types'; 3 | import { ethers } from "hardhat"; 4 | const logger = require('node-color-log'); 5 | 6 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 7 | const {deployments, getNamedAccounts, network} = hre; 8 | let tx; 9 | 10 | logger.color('blue').log("-------------------------------------------------") 11 | logger.color('blue').bold().log("Set price oracle globally...") 12 | 13 | const priceOracle = await deployments.get("PriceOracle") 14 | 15 | // set relay in instant router 16 | const instantRouter = await deployments.get("InstantRouter") 17 | const instantRouterFactory = await ethers.getContractFactory("InstantRouter") 18 | const instantRouterInstance = await instantRouterFactory.attach( 19 | instantRouter.address 20 | ) 21 | 22 | const checkPriceOracleInInstantRouter = await instantRouterInstance.priceOracle() 23 | 24 | if (checkPriceOracleInInstantRouter != priceOracle.address) { 25 | tx = await instantRouterInstance.setPriceOracle( 26 | priceOracle.address 27 | ) 28 | tx.wait(1) 29 | 30 | console.log("set priceOracle in instant router: ", tx.hash) 31 | } else { 32 | console.log("priceOracle is already settled in instant router") 33 | } 34 | 35 | }; 36 | 37 | export default func; 38 | -------------------------------------------------------------------------------- /deploy/013_CcTransferRouterProxy.ts: -------------------------------------------------------------------------------- 1 | import {HardhatRuntimeEnvironment} from 'hardhat/types'; 2 | import {DeployFunction} from 'hardhat-deploy/types'; 3 | import verify from "../helper-functions"; 4 | import config from 'config'; 5 | 6 | import * as dotenv from "dotenv"; 7 | dotenv.config(); 8 | 9 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 10 | const { deployments, getNamedAccounts, network } = hre; 11 | const { deploy } = deployments; 12 | const { deployer } = await getNamedAccounts(); 13 | 14 | const proxyAdmin = config.get("proxy_admin"); 15 | const ccTransferRouterLogic = await deployments.get("CcTransferRouterLogic") 16 | 17 | const deployedContract = await deploy("CcTransferRouterProxy", { 18 | from: deployer, 19 | log: true, 20 | skipIfAlreadyDeployed: true, 21 | args: [ 22 | ccTransferRouterLogic.address, 23 | proxyAdmin, 24 | "0x" 25 | ], 26 | }); 27 | 28 | if (network.name != "hardhat" && process.env.ETHERSCAN_API_KEY && process.env.VERIFY_OPTION == "1") { 29 | await verify( 30 | deployedContract.address, 31 | [ 32 | ccTransferRouterLogic.address, 33 | proxyAdmin, 34 | "0x" 35 | ], 36 | "contracts/routers/CcTransferRouterProxy.sol:CcTransferRouterProxy" 37 | ) 38 | } 39 | }; 40 | 41 | export default func; 42 | func.tags = ["CcTransferRouterProxy"]; 43 | -------------------------------------------------------------------------------- /deploy/015_CcExchangeRouterProx.ts: -------------------------------------------------------------------------------- 1 | import {HardhatRuntimeEnvironment} from 'hardhat/types'; 2 | import {DeployFunction} from 'hardhat-deploy/types'; 3 | import verify from "../helper-functions"; 4 | import config from 'config'; 5 | 6 | import * as dotenv from "dotenv"; 7 | dotenv.config(); 8 | 9 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 10 | const { deployments, getNamedAccounts, network } = hre; 11 | const { deploy } = deployments; 12 | const { deployer } = await getNamedAccounts(); 13 | 14 | const proxyAdmin = config.get("proxy_admin"); 15 | const ccExchangeRouterLogic = await deployments.get("CcExchangeRouterLogic") 16 | 17 | const deployedContract = await deploy("CcExchangeRouterProxy", { 18 | from: deployer, 19 | log: true, 20 | skipIfAlreadyDeployed: true, 21 | args: [ 22 | ccExchangeRouterLogic.address, 23 | proxyAdmin, 24 | "0x" 25 | ], 26 | }); 27 | 28 | if (network.name != "hardhat" && process.env.ETHERSCAN_API_KEY && process.env.VERIFY_OPTION == "1") { 29 | await verify( 30 | deployedContract.address, 31 | [ 32 | ccExchangeRouterLogic.address, 33 | proxyAdmin, 34 | "0x" 35 | ], 36 | "contracts/routers/CcExchangeRouterProxy.sol:CcExchangeRouterProxy" 37 | ) 38 | } 39 | }; 40 | 41 | export default func; 42 | func.tags = ["CcExchangeRouterProxy"]; 43 | -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | skipFiles: [ 3 | 'uniswap/v2-core/UniswapV2ERC20.sol', 4 | 'uniswap/v2-core/UniswapV2Factory.sol', 5 | 'uniswap/v2-core/UniswapV2Pair.sol', 6 | 'uniswap/v2-periphery/UniswapV2Migrator.sol', 7 | 'uniswap/v2-periphery/UniswapV2Router01.sol', 8 | 'uniswap/v2-periphery/UniswapV2Router02.sol', 9 | 'uniswap/v2-core/interfaces/IERC20.sol', 10 | 'uniswap/v2-core/interfaces/IUniswapV2Callee.sol', 11 | 'uniswap/v2-core/interfaces/IUniswapV2ERC20.sol', 12 | 'uniswap/v2-core/interfaces/IUniswapV2Factory.sol', 13 | 'uniswap/v2-core/interfaces/IUniswapV2Pair.sol', 14 | 'uniswap/v2-core/libraries/Math.sol', 15 | 'uniswap/v2-core/libraries/SafeMath.sol', 16 | 'uniswap/v2-core/libraries/UQ112x112.sol', 17 | 'uniswap/v2-periphery/interfaces/IERC20.sol', 18 | 'uniswap/v2-periphery/interfaces/IUniswapV2Migrator.sol', 19 | 'uniswap/v2-periphery/interfaces/IUniswapV2Router01.sol', 20 | 'uniswap/v2-periphery/interfaces/IUniswapV2Router02.sol', 21 | 'uniswap/v2-periphery/interfaces/IWETH.sol', 22 | 'uniswap/v2-periphery/interfaces/V1/IUniswapV1Exchange.sol', 23 | 'uniswap/v2-periphery/interfaces/V1/IUniswapV1Factory.sol', 24 | 'uniswap/v2-periphery/libraries/SafeMath.sol', 25 | 'uniswap/v2-periphery/libraries/TransferHelper.sol', 26 | 'uniswap/v2-periphery/libraries/UniswapV2Library.sol', 27 | ], 28 | configureYulOptimizer: 'true' 29 | }; -------------------------------------------------------------------------------- /contracts/connectors/interfaces/IExchangeConnector.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | interface IExchangeConnector { 5 | 6 | // Events 7 | 8 | event Swap(address[] path, uint[] amounts, address receiver); 9 | 10 | // Read-only functions 11 | 12 | function name() external view returns (string memory); 13 | 14 | function exchangeRouter() external view returns (address); 15 | 16 | function liquidityPoolFactory() external view returns (address); 17 | 18 | function wrappedNativeToken() external view returns (address); 19 | 20 | function getInputAmount( 21 | uint _outputAmount, 22 | address _inputToken, 23 | address _outputToken 24 | ) external view returns (bool, uint); 25 | 26 | function getOutputAmount( 27 | uint _inputAmount, 28 | address _inputToken, 29 | address _outputToken 30 | ) external view returns (bool, uint); 31 | 32 | // State-changing functions 33 | 34 | function setExchangeRouter(address _exchangeRouter) external; 35 | 36 | function setLiquidityPoolFactory() external; 37 | 38 | function setWrappedNativeToken() external; 39 | 40 | function swap( 41 | uint256 _inputAmount, 42 | uint256 _outputAmount, 43 | address[] memory _path, 44 | address _to, 45 | uint256 _deadline, 46 | bool _isFixedToken 47 | ) external returns (bool, uint[] memory); 48 | 49 | function isPathValid(address[] memory _path) external view returns(bool); 50 | } -------------------------------------------------------------------------------- /config/mumbai.json: -------------------------------------------------------------------------------- 1 | { 2 | "proxy_admin": "0x423e64c9c98498E51230B4f7C0EaFd931ED14e7f", 3 | "bitcoin_network": "testnet", 4 | "bitcoin_relay": "0x5b845e3EB38d78cD5006C04b0Bc78692b5535F56", 5 | "starting_block_height": "123", 6 | "uniswap_v2_router_02": "0x8954AfA98594b838bda56FE4C12a09D7739D179b", 7 | "uniswap_v2_factory": "0x5757371414417b8C6CAad45bAeF941aBc7d3Ab32", 8 | "wrapped_native_token": "0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889", 9 | "usdt_token": "0xA02f6adc7926efeBBd59Fd43A84f4E0c0c91e832", 10 | "usdc_token": "0xE097d6B3100777DC31B34dC2c58fB524C2e76921", 11 | "acceptable_delay": 7200, 12 | "chain_id": 137, 13 | "treasury": "0x5364E3557572bd5D5903C0e9C21BE359F2Eac1dA", 14 | "chain_link_oracles": { 15 | "native_token_usd": "0xd0D5e3DB44DE05E9F294BB0a3bEEaF030DE24Ada", 16 | "btc_usd": "0x007A22900a3B98143368Bd5906f8E17e9867581b", 17 | "usdt_usd": "0x92C09849638959196E976289418e5973CC96d645", 18 | "usdc_usd": "0x572dDec9087154dC5dfBB1546Bb62713147e0Ab0" 19 | }, 20 | "cc_transfer": { 21 | "app_id": 1, 22 | "protocol_percentage_fee": 5 23 | }, 24 | "cc_exchange": { 25 | "app_id": 10, 26 | "protocol_percentage_fee": 5 27 | }, 28 | "cc_burn": { 29 | "protocol_percentage_fee": 5, 30 | "slasher_percentage_reward": 500, 31 | "bitcoin_fee": 10000, 32 | "transfer_deadLine": 720 33 | }, 34 | "lockers_contract": { 35 | "minimum_native_locked_amount": "1", 36 | "collateral_ratio": 10, 37 | "liquidation_ratio": 5, 38 | "locker_percentage_fee": 15, 39 | "price_with_discount_ratio": 9000 40 | } 41 | } -------------------------------------------------------------------------------- /config/bsc_testnet.json: -------------------------------------------------------------------------------- 1 | { 2 | "proxy_admin": "0x423e64c9c98498E51230B4f7C0EaFd931ED14e7f", 3 | "bitcoin_network": "testnet", 4 | "bitcoin_relay": "0xE2d527F1dbE6e9639Bb3aC0ae1aDc77627050cF5", 5 | "starting_block_height": "123", 6 | "uniswap_v2_router_02": "0x9Ac64Cc6e4415144C455BD8E4837Fea55603e5c3", 7 | "uniswap_v2_factory": "0xB7926C0430Afb07AA7DEfDE6DA862aE0Bde767bc ", 8 | "wrapped_native_token": "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd", 9 | "usdt_token": "0x337610d27c682E347C9cD60BD4b3b107C9d34dDd", 10 | "usdc_token": "0x64544969ed7EBf5f083679233325356EbE738930", 11 | "acceptable_delay": 7200, 12 | "chain_id": 97, 13 | "treasury": "0x5364E3557572bd5D5903C0e9C21BE359F2Eac1dA", 14 | "chain_link_oracles": { 15 | "native_token_usd": "0x2514895c72f50D8bd4B4F9b1110F0D6bD2c97526", 16 | "btc_usd": "0x5741306c21795FdCBb9b265Ea0255F499DFe515C", 17 | "usdt_usd": "0xEca2605f0BCF2BA5966372C99837b1F182d3D620", 18 | "usdc_usd": "0x90c069C4538adAc136E051052E14c1cD799C41B7" 19 | }, 20 | "cc_transfer": { 21 | "app_id": 1, 22 | "protocol_percentage_fee": 5 23 | }, 24 | "cc_exchange": { 25 | "app_id": 10, 26 | "protocol_percentage_fee": 5 27 | }, 28 | "cc_burn": { 29 | "protocol_percentage_fee": 5, 30 | "slasher_percentage_reward": 500, 31 | "bitcoin_fee": 10000, 32 | "transfer_deadLine": 720 33 | }, 34 | "lockers_contract": { 35 | "minimum_native_locked_amount": "1", 36 | "collateral_ratio": 10, 37 | "liquidation_ratio": 5, 38 | "locker_percentage_fee": 15, 39 | "price_with_discount_ratio": 9000 40 | } 41 | } -------------------------------------------------------------------------------- /config/bsc.json: -------------------------------------------------------------------------------- 1 | { 2 | "proxy_admin": "0x4565aE5c90E52c058410fC7f05711fFed9b6e62a", 3 | "bitcoin_network": "mainnet", 4 | "bitcoin_relay": "0xFcd688999c25D5493571543137cEeb4fbDb44D02", 5 | "starting_block_height": "819636", 6 | "uniswap_v2_router_02": "0x10ED43C718714eb63d5aA57B78B54704E256024E", 7 | "uniswap_v2_factory": "0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73", 8 | "wrapped_native_token": "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", 9 | "usdt_token": "0x55d398326f99059fF775485246999027B3197955", 10 | "usdc_token": "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", 11 | "acceptable_delay": 177, 12 | "chain_id": 56, 13 | "treasury": "0x24004f4f6d2e75b039d528e82b100355d8b1d4fb", 14 | "chain_link_oracles": { 15 | "native_token_usd": "0x0567F2323251f0Aab15c8dFb1967E4e8A7D42aeE", 16 | "btc_usd": "0x264990fbd0A4796A3E3d8E37C4d5F87a3aCa5Ebf", 17 | "usdt_usd": "0xB97Ad0E74fa7d920791E90258A6E2085088b4320", 18 | "usdc_usd": "0x51597f405303C4377E36123cBc172b13269EA163" 19 | }, 20 | "cc_transfer": { 21 | "app_id": 1, 22 | "protocol_percentage_fee": 5 23 | }, 24 | "cc_exchange": { 25 | "app_id": 10, 26 | "protocol_percentage_fee": 5 27 | }, 28 | "cc_burn": { 29 | "protocol_percentage_fee": 5, 30 | "slasher_percentage_reward": 500, 31 | "bitcoin_fee": 15000, 32 | "transfer_deadLine": 720 33 | }, 34 | "lockers_contract": { 35 | "minimum_native_locked_amount": "10000000000000000000", 36 | "collateral_ratio": 1000, 37 | "liquidation_ratio": 500, 38 | "locker_percentage_fee": 15, 39 | "price_with_discount_ratio": 9000 40 | } 41 | } -------------------------------------------------------------------------------- /config/polygon.json: -------------------------------------------------------------------------------- 1 | { 2 | "proxy_admin": "0x4565aE5c90E52c058410fC7f05711fFed9b6e62a", 3 | "bitcoin_network": "mainnet", 4 | "bitcoin_relay": "0x7DeB66341b1d499D7e699589d0cf665De4132EA3", 5 | "starting_block_height": "818599", 6 | "uniswap_v2_router_02": "0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff", 7 | "uniswap_v2_factory": "0x5757371414417b8C6CAad45bAeF941aBc7d3Ab32", 8 | "wrapped_native_token": "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", 9 | "usdt_token": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", 10 | "usdc_token": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", 11 | "acceptable_delay": 177, 12 | "chain_id": 137, 13 | "treasury": "0x24004f4f6d2e75b039d528e82b100355d8b1d4fb", 14 | "chain_link_oracles": { 15 | "native_token_usd": "0xAB594600376Ec9fD91F8e885dADF0CE036862dE0", 16 | "btc_usd": "0xc907E116054Ad103354f2D350FD2514433D57F6f", 17 | "usdt_usd": "0x0A6513e40db6EB1b165753AD52E80663aeA50545", 18 | "usdc_usd": "0xfE4A8cc5b5B2366C1B58Bea3858e81843581b2F7" 19 | }, 20 | "cc_transfer": { 21 | "app_id": 1, 22 | "protocol_percentage_fee": 5 23 | }, 24 | "cc_exchange": { 25 | "app_id": 10, 26 | "protocol_percentage_fee": 5 27 | }, 28 | "cc_burn": { 29 | "protocol_percentage_fee": 5, 30 | "slasher_percentage_reward": 500, 31 | "bitcoin_fee": 15000, 32 | "transfer_deadLine": 720 33 | }, 34 | "lockers_contract": { 35 | "minimum_native_locked_amount": "40000000000000000000000", 36 | "collateral_ratio": 1000, 37 | "liquidation_ratio": 500, 38 | "locker_percentage_fee": 15, 39 | "price_with_discount_ratio": 9000 40 | } 41 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TeleSwap protocol v1 2 | 3 | This repository contains the smart contracts for the TeleSwap protocol. The repository uses Hardhat as a development environment for compilation, testing, and deployment tasks. 4 | 5 | ## What is TeleSwap? 6 | 7 | TeleSwap is a fully decentralized protocol for bridging and exchanging BTC between Bitcoin and EVM chains securely. 8 | 9 | ## Documentation 10 | 11 | See the links below: 12 | - [TeleSwap documentation](https://docs.teleswap.xyz/teleswap/introduction) 13 | - [TeleBTC technical paper](https://arxiv.org/abs/2307.13848) 14 | 15 | ## Audits 16 | - [Quantstamp report](https://github.com/TeleportDAO/audits/blob/main/reports/Quantstamp-Bitcoin-EVM.pdf) (Feb 2023) 17 | 18 | ## Community 19 | - Follow us on [Twitter](https://twitter.com/Teleport_DAO). 20 | - Join our [discord channel](https://discord.com/invite/6RSsgfQgcb). 21 | 22 | ## Install dependencies 23 | 24 | To start, clone the codes and install the required packages using: 25 | 26 | `yarn` 27 | 28 | ## Compile contracts 29 | 30 | To compile the codes, use the below command: 31 | 32 | `yarn clean` & `yarn build` 33 | 34 | ## Run tests 35 | 36 | You can run the entire test suite with the following command: 37 | 38 | `yarn test` 39 | 40 | ## Deploy contracts 41 | 42 | You can deploy contracts on supported networks (mumbai and polygon) with the following command: 43 | 44 | `NETWORK= yarn deploy` 45 | 46 | ## Config contracts 47 | 48 | After deployment, some variables need to be set using the following commands: 49 | 50 | `NETWORK= yarn init_config` 51 | 52 | Run the below command with a different private key to config upgradable contracts: 53 | 54 | `NETWORK= yarn config_upgradables` 55 | -------------------------------------------------------------------------------- /contracts/routers/interfaces/ICcExchangeRouterStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | interface ICcExchangeRouterStorage { 5 | // Structures 6 | 7 | /// @notice Structure for recording cross-chain exchange requests 8 | /// @param appId that user wants to use (which DEX) 9 | /// @param inputAmount Amount of locked BTC on source chain 10 | /// @param outputAmount Amount of output token 11 | /// @param isFixedToken True if amount of input token is fixed 12 | /// @param recipientAddress Address of exchange recipient 13 | /// @param fee Amount of fee that is paid to Teleporter (for tx, relayer and teleporter fees) 14 | /// @param isUsed True if tx has been submitted before 15 | /// @param path Exchange path from input token to output token 16 | /// @param deadline for exchanging tokens 17 | /// @param speed of the request (normal or instant) 18 | struct ccExchangeRequest { 19 | uint appId; 20 | uint inputAmount; 21 | uint outputAmount; 22 | bool isFixedToken; 23 | address recipientAddress; 24 | uint fee; 25 | bool isUsed; 26 | address[] path; 27 | uint deadline; 28 | uint speed; 29 | } 30 | 31 | 32 | // Read-only functions 33 | 34 | function startingBlockNumber() external view returns (uint); 35 | 36 | function protocolPercentageFee() external view returns (uint); 37 | 38 | function chainId() external view returns (uint); 39 | 40 | function relay() external view returns (address); 41 | 42 | function instantRouter() external view returns (address); 43 | 44 | function lockers() external view returns (address); 45 | 46 | function teleBTC() external view returns (address); 47 | 48 | function exchangeConnector(uint appId) external view returns (address); 49 | 50 | function treasury() external view returns (address); 51 | 52 | } -------------------------------------------------------------------------------- /contracts/lockers/LockersStorageStructure.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "./interfaces/ILockersStorage.sol"; 5 | 6 | contract LockersStorageStructure is ILockersStorage { 7 | 8 | // Constants 9 | uint public constant ONE_HUNDRED_PERCENT = 10000; 10 | uint public constant HEALTH_FACTOR = 10000; 11 | uint public constant UPPER_HEALTH_FACTOR = 12500; 12 | uint public constant MAX_LOCKER_FEE = 10000; 13 | uint public constant INACTIVATION_DELAY = 345600; // 4 days (it should be greater than MAX_FINALIZATION_PARAMETER) 14 | uint public constant NATIVE_TOKEN_DECIMAL = 18; 15 | address public constant NATIVE_TOKEN = address(1); 16 | 17 | // Public variables 18 | address public override TeleportDAOToken; 19 | address public override teleBTC; 20 | address public override ccBurnRouter; 21 | address public override exchangeConnector; 22 | address public override priceOracle; 23 | 24 | uint public override minRequiredTDTLockedAmount; 25 | uint public override minRequiredTNTLockedAmount; 26 | uint public override lockerPercentageFee; 27 | uint public override collateralRatio; 28 | uint public override liquidationRatio; 29 | uint public override priceWithDiscountRatio; 30 | uint public override totalNumberOfCandidates; 31 | uint public override totalNumberOfLockers; 32 | 33 | mapping(address => DataTypes.locker) public lockersMapping; // locker's target address -> locker structure 34 | mapping(address => uint) public lockerInactivationTimestamp; 35 | mapping(address => bool) public lockerLeavingAcceptance; 36 | mapping(bytes => address) public lockerTargetAddress; // locker's locking script -> locker's target address 37 | mapping(address => bool) minters; 38 | mapping(address => bool) burners; 39 | 40 | DataTypes.lockersLibConstants public libConstants; 41 | DataTypes.lockersLibParam public libParams; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /contracts/erc20/TokenBatchTransfer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | 7 | contract TokenBatchTransfer is Ownable { 8 | 9 | ERC20 public token; 10 | address public transferOperator; 11 | 12 | modifier onlyOperator() { 13 | require( 14 | msg.sender == transferOperator, 15 | "TokenBatchTransfer: only operator can call this." 16 | ); 17 | _; 18 | } 19 | 20 | constructor(address _token) { 21 | token = ERC20(_token); 22 | transferOperator = msg.sender; 23 | } 24 | 25 | event NewOperator(address transferOperator); 26 | event WithdrawToken(address indexed owner, uint256 stakeAmount); 27 | 28 | function updateOperator(address newOperator) public onlyOwner { 29 | require(newOperator != address(0), "TokenBatchTransfer: Invalid operator"); 30 | transferOperator = newOperator; 31 | emit NewOperator(newOperator); 32 | } 33 | 34 | function withdrawToken(uint256 value) public onlyOperator { 35 | require(token.balanceOf(address(this)) >= value, "TokenBatchTransfer: not enough balance"); 36 | require(token.transfer(msg.sender, value), "TokenBatchTransfer: Unable to transfer tokens"); 37 | emit WithdrawToken(msg.sender, value); 38 | } 39 | 40 | function batchTransfer( 41 | address[] calldata tokenHolders, 42 | uint256[] calldata amounts 43 | ) external onlyOperator { 44 | require(tokenHolders.length == amounts.length, "TokenBatchTransfer: invalid input params"); 45 | 46 | for(uint256 indx = 0; indx < tokenHolders.length; indx++) { 47 | require( 48 | token.transfer(tokenHolders[indx], amounts[indx]), 49 | "TokenBatchTransfer: unable to transfer" 50 | ); 51 | } 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /deploy/014_BurnRouterLogic.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { DeployFunction } from 'hardhat-deploy/types'; 3 | import verify from "../helper-functions" 4 | 5 | require('dotenv').config({path:"../config/temp.env"}); 6 | 7 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 8 | const { deployments, getNamedAccounts, network } = hre; 9 | const { deploy } = deployments; 10 | const { deployer } = await getNamedAccounts(); 11 | 12 | const burnRouterLib = await deploy("BurnRouterLib", { 13 | from: deployer, 14 | log: true, 15 | skipIfAlreadyDeployed: true, 16 | }) 17 | 18 | const deployedContract = await deploy("BurnRouterLogic", { 19 | from: deployer, 20 | log: true, 21 | skipIfAlreadyDeployed: true, 22 | args: [ 23 | // theBlockHeight, 24 | // bitcoinRelay, 25 | // lockersProxy.address, 26 | // treasuryAddress, 27 | // teleBTC.address, 28 | // transferDeadLine, 29 | // protocolPercentageFee, 30 | // slasherPercentageReward, 31 | // bitcoinFee 32 | ], 33 | libraries: { 34 | "BurnRouterLib": burnRouterLib.address 35 | }, 36 | }); 37 | 38 | if (network.name != "hardhat" && process.env.ETHERSCAN_API_KEY && process.env.VERIFY_OPTION == "1") { 39 | await verify( 40 | deployedContract.address, 41 | [ 42 | // theBlockHeight, 43 | // bitcoinRelay, 44 | // lockersProxy.address, 45 | // treasuryAddress, 46 | // teleBTC.address, 47 | // transferDeadLine, 48 | // protocolPercentageFee, 49 | // slasherPercentageReward, 50 | // bitcoinFee 51 | ], 52 | "contracts/routers/BurnRouterLogic.sol:BurnRouterLogic") 53 | } 54 | }; 55 | 56 | export default func; 57 | func.tags = ["BurnRouterLogic"]; 58 | -------------------------------------------------------------------------------- /contracts/erc20/interfaces/ITeleBTC.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; 5 | 6 | interface ITeleBTC is IERC20Upgradeable { 7 | 8 | // Events 9 | event Mint(address indexed doer, address indexed receiver, uint value); 10 | 11 | event Burn(address indexed doer, address indexed burner, uint value); 12 | 13 | event MinterAdded(address indexed newMinter); 14 | 15 | event MinterRemoved(address indexed minter); 16 | 17 | event BurnerAdded(address indexed newBurner); 18 | 19 | event BurnerRemoved(address indexed burner); 20 | 21 | event NewMintLimit(uint oldMintLimit, uint newMintLimit); 22 | 23 | event NewEpochLength(uint oldEpochLength, uint newEpochLength); 24 | 25 | event Blacklisted(address indexed account); 26 | 27 | event UnBlacklisted(address indexed account); 28 | 29 | event BlackListerAdded(address indexed newBlackLister); 30 | 31 | event BlackListerRemoved(address indexed blackLister); 32 | 33 | // read functions 34 | 35 | function decimals() external view returns (uint8); 36 | 37 | // state-changing functions 38 | 39 | function addMinter(address account) external; 40 | 41 | function removeMinter(address account) external; 42 | 43 | function addBurner(address account) external; 44 | 45 | function removeBurner(address account) external; 46 | 47 | function mint(address receiver, uint amount) external returns(bool); 48 | 49 | function burn(uint256 amount) external returns(bool); 50 | 51 | function ownerBurn(address _user, uint _amount) external returns (bool); 52 | 53 | function setMaxMintLimit(uint _mintLimit) external; 54 | 55 | function setEpochLength(uint _length) external; 56 | 57 | function addBlackLister(address account) external; 58 | 59 | function removeBlackLister(address account) external; 60 | 61 | function blacklist(address _account) external; 62 | 63 | function unBlacklist(address _account) external; 64 | } -------------------------------------------------------------------------------- /contracts/routers/BurnRouterStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "./interfaces/IBurnRouterStorage.sol"; 5 | 6 | contract BurnRouterStorage is IBurnRouterStorage { 7 | 8 | // Structures 9 | 10 | /// @notice Structure for recording cc burn requests 11 | /// @param amount of tokens that user wants to burn 12 | /// @param burntAmount that user will receive (after reducing fees from amount) 13 | /// @param sender Address of user who requests burning 14 | /// @param userScript Script hash of the user on Bitcoin 15 | /// @param deadline of locker for executing the request 16 | /// @param isTransferred True if the request has been processed 17 | /// @param scriptType The script type of the user 18 | /// @param requestIdOfLocker The index of the request for a specific locker 19 | struct burnRequest { 20 | uint amount; 21 | uint burntAmount; 22 | address sender; 23 | bytes userScript; 24 | uint deadline; 25 | bool isTransferred; 26 | ScriptTypes scriptType; 27 | uint requestIdOfLocker; 28 | } 29 | 30 | // Constants 31 | uint constant MAX_PROTOCOL_FEE = 10000; 32 | uint constant MAX_SLASHER_REWARD = 10000; 33 | 34 | // Public variables 35 | address public override relay; 36 | address public override lockers; 37 | address public override teleBTC; 38 | address public override treasury; 39 | address public override bitcoinFeeOracle; 40 | uint public override startingBlockNumber; 41 | uint public override transferDeadline; 42 | uint public override protocolPercentageFee; // Min amount is %0.01 43 | uint public override slasherPercentageReward; // Min amount is %1 44 | uint public override bitcoinFee; // Fee of submitting a tx on Bitcoin 45 | 46 | mapping(address => burnRequest[]) public burnRequests; 47 | // ^ Mapping from locker target address to assigned burn requests 48 | mapping(address => uint) public burnRequestCounter; 49 | mapping(bytes32 => bool) public override isUsedAsBurnProof; 50 | // ^ Mapping that shows a txId has been submitted to pay a burn request 51 | 52 | } -------------------------------------------------------------------------------- /contracts/types/DataTypes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "@teleportdao/btc-evm-bridge/contracts/types/ScriptTypesEnum.sol"; 5 | 6 | library DataTypes { 7 | 8 | /// @notice Structure for registering lockers 9 | /// @dev 10 | /// @param lockerLockingScript Locker redeem script 11 | /// @param lockerRescueType Locker script type in case of getting BTCs back 12 | /// @param lockerRescueScript Locker script in case of getting BTCs back 13 | /// @param TDTLockedAmount Bond amount of locker in TDT 14 | /// @param nativeTokenLockedAmount Bond amount of locker in native token of the target chain 15 | /// @param netMinted Total minted - total burnt 16 | /// @param slashingTeleBTCAmount Total amount of teleBTC a locker must be slashed 17 | /// @param reservedNativeTokenForSlash Total native token reserved to support slashing teleBTC 18 | /// @param isLocker Indicates that is already a locker or not 19 | /// @param isCandidate Indicates that is a candidate or not 20 | /// @param isScriptHash Shows if it's script hash 21 | /// has enough collateral to accept more minting requests) 22 | struct locker { 23 | bytes lockerLockingScript; 24 | ScriptTypes lockerRescueType; 25 | bytes lockerRescueScript; 26 | uint TDTLockedAmount; 27 | uint nativeTokenLockedAmount; 28 | uint netMinted; 29 | uint slashingTeleBTCAmount; 30 | uint reservedNativeTokenForSlash; 31 | bool isLocker; 32 | bool isCandidate; 33 | bool isScriptHash; 34 | } 35 | 36 | struct lockersLibConstants { 37 | uint OneHundredPercent; 38 | uint HealthFactor; 39 | uint UpperHealthFactor; 40 | uint MaxLockerFee; 41 | uint NativeTokenDecimal; 42 | address NativeToken; 43 | } 44 | 45 | struct lockersLibParam { 46 | address teleportDAOToken; 47 | address teleBTC; 48 | address ccBurnRouter; 49 | address exchangeConnector; 50 | address priceOracle; 51 | 52 | uint minRequiredTDTLockedAmount; 53 | uint minRequiredTNTLockedAmount; 54 | uint lockerPercentageFee; 55 | uint collateralRatio; 56 | uint liquidationRatio; 57 | uint priceWithDiscountRatio; 58 | uint inactivationDelay; 59 | } 60 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@teleportdao/teleswap-contracts", 3 | "version": "1.1.0", 4 | "description": "Trustless protocol for bridging and trading Bitcoin", 5 | "license": "MIT", 6 | "devDependencies": { 7 | "@commitlint/cli": "^17.4.4", 8 | "@commitlint/config-conventional": "^17.4.4", 9 | "@nomicfoundation/hardhat-chai-matchers": "^1.0.5", 10 | "@nomiclabs/hardhat-ethers": "^2.0.6", 11 | "@nomiclabs/hardhat-etherscan": "^3.0.4", 12 | "@nomiclabs/hardhat-waffle": "^2.0.3", 13 | "@openzeppelin/hardhat-upgrades": "^1.12.0", 14 | "@typechain/ethers-v5": "^7.2.0", 15 | "@typechain/hardhat": "^2.3.1", 16 | "@types/chai": "^4.3.1", 17 | "@types/config": "^0.0.39", 18 | "@types/mocha": "^9.1.1", 19 | "@types/node": "^12.20.54", 20 | "@typescript-eslint/eslint-plugin": "^4.33.0", 21 | "@typescript-eslint/parser": "^4.33.0", 22 | "chai": "^4.3.6", 23 | "dotenv": "^16.0.1", 24 | "eslint": "^7.32.0", 25 | "eslint-config-prettier": "^8.5.0", 26 | "eslint-config-standard": "^16.0.3", 27 | "eslint-plugin-import": "^2.26.0", 28 | "eslint-plugin-node": "^11.1.0", 29 | "eslint-plugin-prettier": "^3.4.1", 30 | "eslint-plugin-promise": "^5.2.0", 31 | "ethereum-waffle": "^3.4.4", 32 | "ethers": "^5.6.8", 33 | "hardhat": "^2.9.9", 34 | "hardhat-contract-sizer": "^2.6.1", 35 | "hardhat-deploy": "^0.11.10", 36 | "hardhat-deploy-tenderly": "^0.1.1", 37 | "hardhat-gas-reporter": "^1.0.8", 38 | "husky": "^8.0.3", 39 | "prettier": "^2.6.2", 40 | "prettier-plugin-solidity": "^1.0.0-beta.19", 41 | "solhint": "^3.3.7", 42 | "solidity-coverage": "^0.7.21", 43 | "ts-node": "^10.8.1", 44 | "typechain": "^5.2.0", 45 | "typescript": "^4.7.3" 46 | }, 47 | "dependencies": { 48 | "@chainlink/contracts": "^0.4.2", 49 | "@liquality/bitcoin-rpc-provider": "^1.13.0", 50 | "@liquality/client": "^1.13.0", 51 | "@openzeppelin/contracts": "4.3.3", 52 | "@openzeppelin/contracts-upgradeable": "^4.7.3", 53 | "@teleportdao/btc-evm-bridge": "1.0.3", 54 | "@uniswap/v2-core": "^1.0.1", 55 | "@uniswap/v2-periphery": "^1.1.0-beta.0", 56 | "config": "^3.3.6", 57 | "node-color-log": "^10.0.2" 58 | }, 59 | "scripts": { 60 | "build": "hardhat compile && tsc", 61 | "clean": "rimraf artifacts && rimraf cache && rimraf deployments/localhost && rimraf src/types/*", 62 | "prepare": "husky install", 63 | "deploy": "NODE_ENV=$NETWORK hardhat deploy --network $NETWORK --export export/abi/$NETWORK.json", 64 | "init_config": "NODE_ENV=$NETWORK hardhat deploy --network $NETWORK --export export/abi/$NETWORK.json --deploy-scripts scripts/init-config", 65 | "update_contracts": "NODE_ENV=$NETWORK hardhat deploy --network $NETWORK --export export/abi/$NETWORK.json --deploy-scripts scripts/update-contracts", 66 | "test": "hardhat test", 67 | "lint": "commitlint --from=HEAD" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /scripts/update-contracts/001_SetRelay.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { DeployFunction } from 'hardhat-deploy/types'; 3 | import { ethers } from "hardhat"; 4 | const logger = require('node-color-log'); 5 | import config from 'config'; 6 | 7 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 8 | const { deployments, getNamedAccounts, network } = hre; 9 | let tx; 10 | 11 | logger.color('blue').log("-------------------------------------------------") 12 | logger.color('blue').bold().log("Set relay globally...") 13 | 14 | const relay = config.get("bitcoin_relay"); 15 | 16 | // set relay in cc transfer router 17 | const ccTransferRouter = await deployments.get("CCTransferRouter") 18 | const ccTransferRouterFactory = await ethers.getContractFactory("CCTransferRouter") 19 | const ccTransferRouterInstance = await ccTransferRouterFactory.attach( 20 | ccTransferRouter.address 21 | ) 22 | 23 | const checkRelayInCCTransfer = await ccTransferRouterInstance.relay() 24 | 25 | if (checkRelayInCCTransfer != relay) { 26 | tx = await ccTransferRouterInstance.setRelay( 27 | relay 28 | ) 29 | tx.wait(1) 30 | console.log("set relay in CCtransfer router: ", tx.hash) 31 | } else { 32 | console.log("relay is already settled in CCtransfer router") 33 | } 34 | 35 | // set relay in cc burn router 36 | const relayHelper = await deployments.get("RelayHelper") 37 | const ccBurnRouter = await deployments.get("CCBurnRouter") 38 | const ccBurnRouterFactory = await ethers.getContractFactory( 39 | "CCBurnRouter", 40 | { 41 | libraries: { 42 | RelayHelper: relayHelper.address 43 | } 44 | } 45 | ) 46 | const ccBurnRouterInstance = await ccBurnRouterFactory.attach( 47 | ccBurnRouter.address 48 | ) 49 | 50 | const checkRelayInCCBurn = await ccBurnRouterInstance.relay() 51 | 52 | if (checkRelayInCCBurn != relay) { 53 | tx = await ccBurnRouterInstance.setRelay( 54 | relay 55 | ) 56 | tx.wait(1) 57 | console.log("set relay in CCburn router: ", tx.hash) 58 | } else { 59 | console.log("relay is already settled in CCburn router") 60 | } 61 | 62 | // set relay in cc exchange router 63 | const ccExchangeRouter = await deployments.get("CCExchangeRouter") 64 | const ccExchangeRouterFactory = await ethers.getContractFactory("CCExchangeRouter") 65 | const ccExchangeRouterInstance = await ccExchangeRouterFactory.attach( 66 | ccExchangeRouter.address 67 | ) 68 | 69 | const checkRelayInCCExchange = await ccExchangeRouterInstance.relay() 70 | 71 | if (checkRelayInCCExchange != relay) { 72 | tx = await ccExchangeRouterInstance.setRelay( 73 | relay 74 | ) 75 | tx.wait(1) 76 | console.log("set relay in CCexchange router: ", tx.hash) 77 | } else { 78 | console.log("relay is already settled in CCexchange router: ") 79 | } 80 | 81 | }; 82 | 83 | export default func; 84 | -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from "dotenv"; 2 | 3 | import { HardhatUserConfig} from "hardhat/config"; 4 | import { HttpNetworkUserConfig } from "hardhat/types"; 5 | import '@openzeppelin/hardhat-upgrades'; 6 | 7 | import "@nomiclabs/hardhat-etherscan"; 8 | import "@nomiclabs/hardhat-waffle"; 9 | import "@typechain/hardhat"; 10 | import "hardhat-gas-reporter"; 11 | import "solidity-coverage"; 12 | import "hardhat-deploy"; 13 | import "hardhat-deploy-tenderly"; 14 | import "hardhat-contract-sizer"; 15 | 16 | dotenv.config(); 17 | 18 | const infuraNetwork = ( 19 | accounts: any, 20 | network: string, 21 | chainId?: number, 22 | gas?: number 23 | ): HttpNetworkUserConfig => { 24 | return { 25 | url: `https://${network}.infura.io/v3/${process.env.PROJECT_ID}`, 26 | chainId, 27 | gas, 28 | accounts, 29 | gasPrice: 200000000000, 30 | } 31 | } 32 | 33 | const config: HardhatUserConfig = { 34 | solidity: { 35 | compilers: [ 36 | { 37 | version: "0.5.16", 38 | settings: { 39 | optimizer: { 40 | enabled: true 41 | }, 42 | }, 43 | }, 44 | { 45 | version: "0.6.6", 46 | settings: { 47 | optimizer: { 48 | enabled: true 49 | }, 50 | }, 51 | }, 52 | { 53 | version: "0.7.6", 54 | settings: { 55 | optimizer: { 56 | enabled: true 57 | }, 58 | }, 59 | }, 60 | { 61 | version: "0.8.0", 62 | settings: { 63 | optimizer: { 64 | enabled: true 65 | }, 66 | }, 67 | }, 68 | { 69 | version: "0.8.2", 70 | settings: { 71 | optimizer: { 72 | enabled: true 73 | }, 74 | }, 75 | } 76 | ], 77 | }, 78 | networks: { 79 | mainnet: infuraNetwork( 80 | process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], 81 | "mainnet", 82 | 1, 83 | 6283185, 84 | ), 85 | goerli: infuraNetwork( 86 | process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], 87 | "goerli", 88 | 5, 89 | 6283185 90 | ), 91 | polygon: { 92 | url: "https://rpc-mainnet.maticvigil.com/", 93 | chainId: 137, 94 | accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [] 95 | }, 96 | mumbai: { 97 | url: "https://rpc-mumbai.maticvigil.com", 98 | chainId: 80001, 99 | accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], 100 | }, 101 | bsc: { 102 | url: "https://bsc-dataseed.binance.org/", 103 | chainId: 56, 104 | accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], 105 | }, 106 | bsc_testnet: { 107 | url: "https://bsc-testnet.publicnode.com", 108 | chainId: 97, 109 | accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], 110 | }, 111 | }, 112 | paths: { 113 | artifacts: "artifacts", 114 | deploy: "deploy", 115 | deployments: "deployments", 116 | }, 117 | typechain: { 118 | outDir: "src/types", 119 | target: "ethers-v5", 120 | }, 121 | namedAccounts: { 122 | deployer: { 123 | default: 0, 124 | }, 125 | }, 126 | gasReporter: { 127 | // enabled: process.env.REPORT_GAS !== undefined, 128 | enabled: true, 129 | currency: "USD", 130 | }, 131 | etherscan: { 132 | apiKey: process.env.ETHERSCAN_API_KEY, 133 | }, 134 | }; 135 | 136 | export default config; 137 | -------------------------------------------------------------------------------- /scripts/init-config/004-add-liquidity-for-testnet.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { DeployFunction } from 'hardhat-deploy/types'; 3 | import { ethers } from "hardhat"; 4 | import { BigNumber } from "ethers"; 5 | import config from 'config' 6 | const logger = require('node-color-log'); 7 | 8 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 9 | const { deployments, getNamedAccounts } = hre; 10 | const { deployer } = await getNamedAccounts(); 11 | 12 | const one8Dec = BigNumber.from(10).pow(8).mul(1) 13 | const one18Dec = BigNumber.from(10).pow(18).mul(1) 14 | 15 | const bitcoinNetwork = config.get("bitcoin_network"); 16 | const wrappedMatic = config.get("wrapped_native_token") as string; 17 | const uniswapFactory = await config.get("uniswap_v2_factory") as string 18 | const uniswapRouter = await config.get("uniswap_v2_router_02") as string 19 | 20 | // const teleBTC = await deployments.get("TeleBTC") 21 | const teleBTC = await deployments.get("TeleBTCProxy") 22 | 23 | const teleBTCFactory = await ethers.getContractFactory("TeleBTCLogic"); 24 | const teleBTCInstance = await teleBTCFactory.attach( 25 | teleBTC.address 26 | ); 27 | 28 | const uniswapFactoryFactory = await ethers.getContractFactory("UniswapV2Factory"); 29 | const uniswapFactoryInstance = await uniswapFactoryFactory.attach( 30 | uniswapFactory 31 | ); 32 | 33 | const uniswapRouterFactory = await ethers.getContractFactory("UniswapV2Router02"); 34 | const uniswapRouterInstance = await uniswapRouterFactory.attach( 35 | uniswapRouter 36 | ); 37 | 38 | const theLiquidityPair2 = await uniswapFactoryInstance.getPair( 39 | teleBTC.address, 40 | wrappedMatic 41 | ) 42 | 43 | if (bitcoinNetwork == "testnet" && theLiquidityPair2 == "0x0000000000000000000000000000000000000000") { 44 | 45 | logger.color('blue').log("-------------------------------------------------") 46 | logger.color('blue').bold().log("Charge WMATIC-TELEBTC pool ...") 47 | 48 | const timeNow = Date.now() 49 | const unixTimeNow = (timeNow - (timeNow % 1000))/1000 + 1000 50 | 51 | const isMinterTeleBTCTx = await teleBTCInstance.minters(deployer) 52 | 53 | if(!isMinterTeleBTCTx) { 54 | const addMinterTeleBTCTx = await teleBTCInstance.addMinter(deployer) 55 | await addMinterTeleBTCTx.wait(1) 56 | } 57 | 58 | if ((await teleBTCInstance.totalSupply()) == 0) { 59 | const mintTeleBTCTx = await teleBTCInstance.mint(deployer, one8Dec.div(2)) 60 | await mintTeleBTCTx.wait(1) 61 | } 62 | 63 | let approveTeleBTCTx = await teleBTCInstance.approve( 64 | uniswapRouter, 65 | one8Dec.div(2) 66 | ) 67 | await approveTeleBTCTx.wait(1) 68 | 69 | const addLiquidityPairTx = await uniswapRouterInstance.addLiquidityETH( 70 | teleBTC.address, 71 | (one8Dec.div(1800)).toString(), 72 | 0, 73 | 0, 74 | deployer, 75 | unixTimeNow, 76 | { 77 | value: (one18Dec.mul(10)).toString() 78 | } 79 | ) 80 | 81 | await addLiquidityPairTx.wait(1) 82 | console.log("Charged WMATIC-TELEBTC pool: ", addLiquidityPairTx.hash) 83 | } 84 | }; 85 | 86 | export default func; 87 | -------------------------------------------------------------------------------- /contracts/erc20/Returner.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.0 <0.8.4; 2 | 3 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; 6 | import "@openzeppelin/contracts/security/Pausable.sol"; 7 | 8 | contract Returner is Ownable, ReentrancyGuard, Pausable { 9 | struct SellerParams { 10 | string tokenName; 11 | uint256 tokenAmount; 12 | uint256 teleBTCAmount; 13 | } 14 | mapping (address => SellerParams) public seller; 15 | 16 | struct BurnerParams { 17 | string tokenName; 18 | uint256 tokenAmount; 19 | } 20 | mapping (address => BurnerParams) public burner; 21 | 22 | mapping (string => address) public tokenMapping; 23 | 24 | address public teleBTC; 25 | 26 | function pause() external onlyOwner { 27 | _pause(); 28 | } 29 | 30 | function unpause() external onlyOwner { 31 | _unpause(); 32 | } 33 | 34 | constructor(string[] memory tokenName, address[] memory tokenAddress, address _teleBTC) public { 35 | require(tokenName.length == tokenAddress.length, "Invalid input parameters"); 36 | for (uint i = 0; i < tokenName.length; i++) 37 | tokenMapping[tokenName[i]] = tokenAddress[i]; 38 | teleBTC = _teleBTC; 39 | } 40 | 41 | function changeToken(string memory tokenName, address tokenAddress) public onlyOwner { 42 | tokenMapping[tokenName] = tokenAddress; 43 | } 44 | 45 | function addTeleBTCSeller(address account, string memory tokenName, uint256 tokenAmount, uint256 teleBTCAmount) public onlyOwner{ 46 | seller[account] = SellerParams( 47 | tokenName, 48 | tokenAmount, 49 | teleBTCAmount 50 | ); 51 | } 52 | 53 | function addBurner(address account, string memory tokenName, uint256 tokenAmount) public onlyOwner{ 54 | burner[account] = BurnerParams( 55 | tokenName, 56 | tokenAmount 57 | ); 58 | } 59 | 60 | function sellTeleBTC(address account) nonReentrant whenNotPaused public payable { 61 | require(account == msg.sender, "not owner"); 62 | require(seller[account].tokenAmount != 0, "not seller"); 63 | require( 64 | ERC20(teleBTC).transferFrom(account, address(this), seller[account].teleBTCAmount), 65 | "Unable to transfer TeleBTC to contract" 66 | ); 67 | require( 68 | ERC20(tokenMapping[seller[account].tokenName]).transfer(account, seller[account].tokenAmount), 69 | "Unable to transfer token to the account" 70 | ); 71 | seller[account].tokenAmount = 0; 72 | } 73 | 74 | function refund(address account) nonReentrant whenNotPaused public payable { 75 | require(account == msg.sender, "not owner"); 76 | require(burner[account].tokenAmount != 0, "not burner"); 77 | require( 78 | ERC20(tokenMapping[burner[account].tokenName]).transfer(account, burner[account].tokenAmount), 79 | "Unable to transfer token to the account" 80 | ); 81 | burner[account].tokenAmount = 0; 82 | } 83 | 84 | function withdrawToken(string memory tokenName, uint256 tokenAmount) public onlyOwner{ 85 | require(ERC20(tokenMapping[tokenName]).balanceOf(address(this)) >= tokenAmount, "Not enough balance in the contract"); 86 | ERC20(tokenMapping[tokenName]).transfer(msg.sender, tokenAmount); 87 | } 88 | } -------------------------------------------------------------------------------- /test/test_fixtures/ccBurnRequests.json: -------------------------------------------------------------------------------- 1 | { 2 | "burnProof_valid": { 3 | "txId" : "0x3bc193f1c3d40f1550ea31893da99120ab264cbc702208c21fc0065e8bc1d2a8", 4 | "version": "0x02000000", 5 | "vin": "0x01df4a990ad3c3a225862465bb660f06d445914a038ada819ace235afb9f23cff30200000000feffffff", 6 | "vout": "0x0200e1f505000000001976a91412ab8dc588ca9d5787dde7eb29569da63c3a238c88ac00e1f505000000001976a914748284390f9e263a4b766a75d0633c50426eb87587ac", 7 | "locktime": "0x00000000", 8 | "intermediateNodes": "0x7451e7cd7a5afcd93d5a3f84e4d7976fb3bd771dc6aeab416d818ea1d72c0476" 9 | }, 10 | "burnProof_validP2WPKH": { 11 | "txId": "0x9111c946df1b515a1a2a7a0d80124949829ba723a2fa20def361de6b3322fcb6", 12 | "version": "0x02000000", 13 | "vin": "0x01df4a990ad3c3a225862465bb660f06d445914a038ada819ace235afb9f23cff30200000000feffffff", 14 | "vout": "0x0200e1f50500000000160014751e76e8199196d454941c45d1b3a323f1433bd600e1f505000000001976a914748284390f9e263a4b766a75d0633c50426eb87587ac", 15 | "locktime": "0x00000000", 16 | "intermediateNodes": "0x7451e7cd7a5afcd93d5a3f84e4d7976fb3bd771dc6aeab416d818ea1d72c0476" 17 | }, 18 | "burnProof_validWithoutChange": { 19 | "txId": "0x5c91f47b3fb76bc0b4433fee7968aedb3081484de526095fdba763e39558ce46", 20 | "version": "0x02000000", 21 | "vin": "0x01df4a990ad3c3a225862465bb660f06d445914a038ada819ace235afb9f23cff30200000000feffffff", 22 | "vout": "0x0100e1f505000000001976a91412ab8dc588ca9d5787dde7eb29569da63c3a238c88ac", 23 | "locktime": "0x00000000", 24 | "intermediateNodes": "0x7451e7cd7a5afcd93d5a3f84e4d7976fb3bd771dc6aeab416d818ea1d72c0476" 25 | }, 26 | "burnProof_invalidChange": { 27 | "txId": "0xca36c95e99efb70de74c98832a1fd8141acb86229eab4130bd9894052c279703", 28 | "version": "0x02000000", 29 | "vin": "0x01df4a990ad3c3a225862465bb660f06d445914a038ada819ace235afb9f23cff30200000000feffffff", 30 | "vout": "0x0200e1f505000000001976a91412ab8dc588ca9d5787dde7eb29569da63c3a238c88ac00e1f505000000001976a914748284390f9e263a4b766a75d0633c50426eb87588ac", 31 | "locktime": "0x00000000", 32 | "intermediateNodes": "0x7451e7cd7a5afcd93d5a3f84e4d7976fb3bd771dc6aeab416d818ea1d72c0476" 33 | }, 34 | "disputeLocker_input": { 35 | "txId": "0xcc71d5eeeb0c5bfa8a1bcb6243287e48cd30ff96c954a328323f336192c05ae9", 36 | "version": "0x02000000", 37 | "vin": "0x013BC193F1C3D40F1550EA31893DA99120AB264CBC702208C21FC0065E8BC1D2A80100000000feffffff", 38 | "vout": "0x0200e1f505000000001976a91412ab8dc588ca9d5787dde7eb29569da63c3a238c88ac00e1f505000000001976a914748284390f9e263a4b766a75d0633c50426eb87587ac", 39 | "locktime": "0x00000000", 40 | "intermediateNodes": "0x7451e7cd7a5afcd93d5a3f84e4d7976fb3bd771dc6aeab416d818ea1d72c0476", 41 | "OutputValue": 200000000 42 | }, 43 | "disputeLocker_output": { 44 | "txId": "0x3BC193F1C3D40F1550EA31893DA99120AB264CBC702208C21FC0065E8BC1D2A8", 45 | "version": "0x02000000", 46 | "vin": "0x01df4a990ad3c3a225862465bb660f06d445914a038ada819ace235afb9f23cff30200000000feffffff", 47 | "vout": "0x0200e1f505000000001976a91412ab8dc588ca9d5787dde7eb29569da63c3a238c88ac00e1f505000000001976a914748284390f9e263a4b766a75d0633c50426eb87587ac", 48 | "locktime": "0x00000000", 49 | "intermediateNodes": "0x7451e7cd7a5afcd93d5a3f84e4d7976fb3bd771dc6aeab416d818ea1d72c0476" 50 | }, 51 | "disputeLocker_invalidOutput": { 52 | "txId": "0xa8d2c18b5e06c01fc2082270bc4c26ab2091a93d8931ea50150fd4c3f193c13b", 53 | "version": "0x02000000", 54 | "vin": "0x01af4a990ad3c3a225862465bb660f06d445914a038ada819ace235afb9f23cff30200000000feffffff", 55 | "vout": "0x0200e1f505000000001976a91412ab8dc588ca9d5787dde7eb29569da63c3a238c88ac00e1f505000000001976a914748284390f9e263a4b766a75d0633c50426eb87587ac", 56 | "locktime": "0x00000000", 57 | "intermediateNodes": "0x7451e7cd7a5afcd93d5a3f84e4d7976fb3bd771dc6aeab416d818ea1d72c0476" 58 | } 59 | } -------------------------------------------------------------------------------- /contracts/routers/interfaces/ICcTransferRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | interface ICcTransferRouter { 5 | 6 | // Events 7 | 8 | /// @notice Emits when a cc transfer request gets done 9 | /// @param lockerLockingScript Locking script of the locker on bitcoin network 10 | /// @param lockerScriptType Script type of the locker locking script 11 | /// @param lockerTargetAddress Address of the locker on EVM based target chain 12 | /// @param user Address of teleBTC recipient 13 | /// @param inputAmount Amount of tokens that user locked on source chain 14 | /// @param receivedAmount Amount of tokens that user receives 15 | /// @param speed Speed of the request (normal or instant) 16 | /// @param teleporter Address of teleporter who submitted the request 17 | /// @param teleporterFee Amount of fee that is paid to Teleporter (tx, relayer and teleporter fees) 18 | /// @param relayFee Amount of fee that is paid to relay contract 19 | /// @param protocolFee Amount of fee that is paid to the protocol 20 | /// @param bitcoinTxId Address of teleporter who submitted the request 21 | event CCTransfer( 22 | bytes indexed lockerLockingScript, 23 | uint lockerScriptType, 24 | address lockerTargetAddress, 25 | address indexed user, 26 | uint inputAmount, 27 | uint receivedAmount, 28 | uint indexed speed, 29 | address teleporter, 30 | uint teleporterFee, 31 | uint relayFee, 32 | uint protocolFee, 33 | bytes32 bitcoinTxId 34 | ); 35 | 36 | /// @notice Emits when changes made to relay address 37 | event NewRelay ( 38 | address oldRelay, 39 | address newRelay 40 | ); 41 | 42 | /// @notice Emits when changes made to InstantRouter address 43 | event NewInstantRouter ( 44 | address oldInstantRouter, 45 | address newInstantRouter 46 | ); 47 | 48 | /// @notice Emits when changes made to Lockers address 49 | event NewLockers ( 50 | address oldLockers, 51 | address newLockers 52 | ); 53 | 54 | /// @notice Emits when changes made to TeleBTC address 55 | event NewTeleBTC ( 56 | address oldTeleBTC, 57 | address newTeleBTC 58 | ); 59 | 60 | /// @notice Emits when changes made to protocol percentage fee 61 | event NewProtocolPercentageFee ( 62 | uint oldProtocolPercentageFee, 63 | uint newProtocolPercentageFee 64 | ); 65 | 66 | /// @notice Emits when changes made to Treasury address 67 | event NewTreasury ( 68 | address oldTreasury, 69 | address newTreasury 70 | ); 71 | 72 | // Read-only functions 73 | 74 | function isRequestUsed(bytes32 _txId) external view returns (bool); 75 | 76 | // State-changing functions 77 | 78 | function setStartingBlockNumber(uint _startingBlockNumber) external; 79 | 80 | function setRelay(address _relay) external; 81 | 82 | function setInstantRouter(address _instantRouter) external; 83 | 84 | function setLockers(address _lockers) external; 85 | 86 | function setTeleBTC(address _teleBTC) external; 87 | 88 | function setTreasury(address _treasury) external; 89 | 90 | function setProtocolPercentageFee(uint _protocolPercentageFee) external; 91 | 92 | function ccTransfer( 93 | // Bitcoin tx 94 | bytes4 _version, 95 | bytes memory _vin, 96 | bytes calldata _vout, 97 | bytes4 _locktime, 98 | // Bitcoin block number 99 | uint256 _blockNumber, 100 | // Merkle proof 101 | bytes calldata _intermediateNodes, 102 | uint _index, 103 | bytes calldata _lockerLockingScript 104 | ) external payable returns (bool); 105 | } -------------------------------------------------------------------------------- /contracts/oracle/interfaces/IPriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | interface IPriceOracle { 5 | 6 | /// @notice Emits when new exchange router is added 7 | /// @param exchangeRouter Address of new exchange router 8 | /// @param exchangeConnector Address of exchange connector 9 | event ExchangeConnectorAdded(address indexed exchangeRouter, address indexed exchangeConnector); 10 | 11 | /// @notice Emits when an exchange router is removed 12 | /// @param exchangeRouter Address of removed exchange router 13 | event ExchangeConnectorRemoved(address indexed exchangeRouter); 14 | 15 | /// @notice Emits when a price proxy is set 16 | /// @param _token Address of the token 17 | /// @param _priceProxyAddress Address of price proxy contract 18 | event SetPriceProxy(address indexed _token, address indexed _priceProxyAddress); 19 | 20 | /// @notice Emits when changes made to acceptable delay 21 | event NewAcceptableDelay(uint oldAcceptableDelay, uint newAcceptableDelay); 22 | 23 | /// @notice Emits when changes made to oracle native token 24 | event NewOracleNativeToken(address indexed oldOracleNativeToken, address indexed newOracleNativeToken); 25 | 26 | // Read-only functions 27 | 28 | /// @notice Gives USD price proxy address for a token 29 | /// @param _token Address of the token 30 | /// @return Address of price proxy contract 31 | function ChainlinkPriceProxy(address _token) external view returns (address); 32 | 33 | /// @notice Gives exchange connector address for an exchange router 34 | /// @param _exchangeRouter Address of exchange router 35 | /// @return Address of exchange connector 36 | function exchangeConnector(address _exchangeRouter) external view returns (address); 37 | 38 | /// @notice Gives address of an exchange router from exchange routers list 39 | /// @param _index Index of exchange router 40 | /// @return Address of exchange router 41 | function exchangeRoutersList(uint _index) external view returns (address); 42 | 43 | function getExchangeRoutersListLength() external view returns (uint); 44 | 45 | function acceptableDelay() external view returns (uint); 46 | 47 | function oracleNativeToken() external view returns (address); 48 | 49 | function equivalentOutputAmountByAverage( 50 | uint _inputAmount, 51 | uint _inputDecimals, 52 | uint _outputDecimals, 53 | address _inputToken, 54 | address _outputToken 55 | ) external view returns (uint); 56 | 57 | function equivalentOutputAmount( 58 | uint _inputAmount, 59 | uint _inputDecimals, 60 | uint _outputDecimals, 61 | address _inputToken, 62 | address _outputToken 63 | ) external view returns (uint); 64 | 65 | function equivalentOutputAmountFromOracle( 66 | uint _inputAmount, 67 | uint _inputDecimals, 68 | uint _outputDecimals, 69 | address _inputToken, 70 | address _outputToken 71 | ) external view returns (uint); 72 | 73 | function equivalentOutputAmountFromExchange( 74 | address _exchangeRouter, 75 | uint _inputAmount, 76 | address _inputToken, 77 | address _outputToken 78 | ) external view returns (uint); 79 | 80 | // State-changing functions 81 | 82 | function addExchangeConnector(address _exchangeRouter, address _exchangeConnector) external; 83 | 84 | function removeExchangeConnector(uint _exchangeRouterIndex) external; 85 | 86 | function setPriceProxy(address _token, address _priceProxyAddress) external; 87 | 88 | function setAcceptableDelay(uint _acceptableDelay) external; 89 | 90 | function setOracleNativeToken(address _oracleNativeToken) external; 91 | } -------------------------------------------------------------------------------- /scripts/update-contracts/003_SetTeleBTC.ts: -------------------------------------------------------------------------------- 1 | import {HardhatRuntimeEnvironment} from 'hardhat/types'; 2 | import {DeployFunction} from 'hardhat-deploy/types'; 3 | import { ethers } from "hardhat"; 4 | const logger = require('node-color-log'); 5 | 6 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 7 | const { deployments } = hre; 8 | let tx; 9 | 10 | logger.color('blue').log("-------------------------------------------------") 11 | logger.color('blue').bold().log("Set telebtc globally...") 12 | 13 | // const telebtc = await deployments.get("TeleBTC") 14 | const telebtc = await deployments.get("TeleBTCProxy") 15 | 16 | // set relay in cc transfer router 17 | const ccTransferRouter = await deployments.get("CCTransferRouter") 18 | const ccTransferRouterFactory = await ethers.getContractFactory("CCTransferRouter") 19 | const ccTransferRouterInstance = await ccTransferRouterFactory.attach( 20 | ccTransferRouter.address 21 | ) 22 | 23 | const checkTeleBTCInCCTransfer = await ccTransferRouterInstance.teleBTC() 24 | 25 | if (checkTeleBTCInCCTransfer != telebtc.address) { 26 | tx = await ccTransferRouterInstance.setTeleBTC( 27 | telebtc.address 28 | ) 29 | tx.wait(1) 30 | console.log("set teleBTC in CCtransfer router: ", tx.hash) 31 | } else { 32 | console.log("teleBTC is already settled in CCtransfer router") 33 | } 34 | 35 | // set relay in cc burn router 36 | const relayHelper = await deployments.get("RelayHelper") 37 | const ccBurnRouter = await deployments.get("CCBurnRouter") 38 | const ccBurnRouterFactory = await ethers.getContractFactory( 39 | "CCBurnRouter", 40 | { 41 | libraries: { 42 | RelayHelper: relayHelper.address 43 | } 44 | } 45 | ) 46 | const ccBurnRouterInstance = await ccBurnRouterFactory.attach( 47 | ccBurnRouter.address 48 | ) 49 | 50 | const checkTeleBTCInCCBurn = await ccBurnRouterInstance.teleBTC() 51 | 52 | if (checkTeleBTCInCCBurn != telebtc.address) { 53 | tx = await ccBurnRouterInstance.setTeleBTC( 54 | telebtc.address 55 | ) 56 | tx.wait(1) 57 | console.log("set telebtc in CCburn router: ", tx.hash) 58 | } else { 59 | console.log("telebtc is already settled in CCburn router") 60 | } 61 | 62 | // set telebtc in cc exchange router 63 | const ccExchangeRouter = await deployments.get("CCExchangeRouter") 64 | const ccExchangeRouterFactory = await ethers.getContractFactory("CCExchangeRouter") 65 | const ccExchangeRouterInstance = await ccExchangeRouterFactory.attach( 66 | ccExchangeRouter.address 67 | ) 68 | 69 | const checkTeleBTCInCCExchange = await ccExchangeRouterInstance.teleBTC() 70 | 71 | if (checkTeleBTCInCCExchange != telebtc.address) { 72 | tx = await ccExchangeRouterInstance.setTeleBTC( 73 | telebtc.address 74 | ) 75 | tx.wait(1) 76 | console.log("set telebtc in CCexchange router: ", tx.hash) 77 | } else { 78 | console.log("telebtc is already settled in CCexchange router") 79 | } 80 | 81 | // set telebtc in locker 82 | // const lockers = await deployments.get("LockersProxy") 83 | // const lockersLib = await deployments.get("LockersLib") 84 | // const lockersFactory = await ethers.getContractFactory( 85 | // "LockersLogicTestnet", 86 | // { 87 | // libraries: { 88 | // LockersLib: lockersLib.address 89 | // } 90 | // } 91 | // ); 92 | // const lockersInstance = await lockersFactory.attach( 93 | // lockers.address 94 | // ) 95 | 96 | // tx = await lockersInstance.setTeleBTC( 97 | // telebtc.address 98 | // ) 99 | // tx.wait(1) 100 | // console.log("set telebtc in lockers: ", tx.hash) 101 | 102 | logger.color('blue').log("-------------------------------------------------") 103 | 104 | }; 105 | 106 | export default func; 107 | -------------------------------------------------------------------------------- /contracts/routers/interfaces/ICcExchangeRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | interface ICcExchangeRouter { 5 | 6 | // Events 7 | 8 | /// @notice Emits when a cc exchange request gets done 9 | /// @param user Exchange recipient address 10 | /// @param speed Speed of the request (normal or instant) 11 | /// @param teleporter Address of teleporter who submitted the request 12 | /// @param teleporterFee Amount of fee that is paid to Teleporter (tx, relayer and teleporter fees) 13 | event CCExchange( 14 | bytes lockerLockingScript, 15 | uint lockerScriptType, 16 | address lockerTargetAddress, 17 | address indexed user, 18 | address[2] inputAndOutputToken, 19 | uint[2] inputAndOutputAmount, 20 | uint indexed speed, 21 | address indexed teleporter, 22 | uint teleporterFee, 23 | bytes32 bitcoinTxId, 24 | uint appId 25 | ); 26 | 27 | /// @notice Emits when a cc exchange request fails 28 | /// @dev In this case, instead of excahnging tokens, 29 | /// we mint teleBTC and send it to the user 30 | /// @param recipientAddress Exchange recipient address 31 | /// @param speed Speed of the request (normal or instant) 32 | /// @param teleporter Address of teleporter who submitted the request 33 | /// @param teleporterFee Amount of fee that is paid to Teleporter (tx, relayer and teleporter fees) 34 | event FailedCCExchange( 35 | bytes lockerLockingScript, 36 | uint lockerScriptType, 37 | address lockerTargetAddress, 38 | address indexed recipientAddress, 39 | address[2] inputAndOutputToken, 40 | uint[2] inputAndOutputAmount, 41 | uint indexed speed, 42 | address indexed teleporter, 43 | uint teleporterFee, 44 | bytes32 bitcoinTxId, 45 | uint appId 46 | ); 47 | 48 | /// @notice Emits when appId for an exchange connector is set 49 | /// @param appId Assigned application id to exchange 50 | /// @param exchangeConnector Address of exchange connector contract 51 | event SetExchangeConnector( 52 | uint appId, 53 | address exchangeConnector 54 | ); 55 | 56 | /// @notice Emits when changes made to relay address 57 | event NewRelay ( 58 | address oldRelay, 59 | address newRelay 60 | ); 61 | 62 | /// @notice Emits when changes made to InstantRouter address 63 | event NewInstantRouter ( 64 | address oldInstantRouter, 65 | address newInstantRouter 66 | ); 67 | 68 | /// @notice Emits when changes made to Lockers address 69 | event NewLockers ( 70 | address oldLockers, 71 | address newLockers 72 | ); 73 | 74 | /// @notice Emits when changes made to TeleBTC address 75 | event NewTeleBTC ( 76 | address oldTeleBTC, 77 | address newTeleBTC 78 | ); 79 | 80 | /// @notice Emits when changes made to protocol percentage fee 81 | event NewProtocolPercentageFee ( 82 | uint oldProtocolPercentageFee, 83 | uint newProtocolPercentageFee 84 | ); 85 | 86 | /// @notice Emits when changes made to Treasury address 87 | event NewTreasury ( 88 | address oldTreasury, 89 | address newTreasury 90 | ); 91 | 92 | // Read-only functions 93 | 94 | function isRequestUsed(bytes32 _txId) external view returns (bool); 95 | 96 | // State-changing functions 97 | 98 | function setStartingBlockNumber(uint _startingBlockNumber) external; 99 | 100 | function setRelay(address _relay) external; 101 | 102 | function setInstantRouter(address _instantRouter) external; 103 | 104 | function setLockers(address _lockers) external; 105 | 106 | function setTeleBTC(address _teleBTC) external; 107 | 108 | function setExchangeConnector(uint _appId, address _exchangeConnector) external; 109 | 110 | function setTreasury(address _treasury) external; 111 | 112 | function setProtocolPercentageFee(uint _protocolPercentageFee) external; 113 | 114 | function ccExchange( 115 | bytes4 _version, 116 | bytes memory _vin, 117 | bytes calldata _vout, 118 | bytes4 _locktime, 119 | // ^ Bitcoin tx 120 | uint256 _blockNumber, // Bitcoin block number 121 | bytes calldata _intermediateNodes, // Merkle proof 122 | uint _index, 123 | bytes calldata _lockerLockingScript 124 | ) external payable returns(bool); 125 | } -------------------------------------------------------------------------------- /contracts/libraries/RequestHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | // A library for parsing cc transfer and cc exchange requests 5 | library RequestHelper { 6 | 7 | /// @notice Returns chain id of the request 8 | /// @dev Determines the chain that request belongs to 9 | /// @param _arbitraryData Data written in Bitcoin tx 10 | /// @return parsedValue The parsed value of chain id 11 | function parseChainId(bytes memory _arbitraryData) internal pure returns (uint8 parsedValue) { 12 | bytes memory slicedBytes = sliceBytes(_arbitraryData, 0, 0); 13 | assembly { 14 | parsedValue := mload(add(slicedBytes, 1)) 15 | } 16 | } 17 | 18 | /// @notice Returns app id of the request 19 | /// @dev Determines the app that request belongs to (e.g. cross-chain transfer app id is 0) 20 | /// @param _arbitraryData Data written in Bitcoin tx 21 | /// @return parsedValue The parsed value of app id 22 | function parseAppId(bytes memory _arbitraryData) internal pure returns (uint16 parsedValue) { 23 | bytes memory slicedBytes = sliceBytes(_arbitraryData, 1, 2); 24 | assembly { 25 | parsedValue := mload(add(slicedBytes, 2)) 26 | } 27 | } 28 | 29 | /// @notice Returns recipient address 30 | /// @dev Minted TeleBTC or exchanged tokens will be sent to this address 31 | /// @param _arbitraryData Data written in Bitcoin tx 32 | /// @return parsedValue The parsed value of recipient address 33 | function parseRecipientAddress(bytes memory _arbitraryData) internal pure returns (address parsedValue) { 34 | bytes memory slicedBytes = sliceBytes(_arbitraryData, 3, 22); 35 | assembly { 36 | parsedValue := mload(add(slicedBytes, 20)) 37 | } 38 | } 39 | 40 | /// @notice Returns percentage fee (from total minted TeleBTC) 41 | /// @dev This fee goes to Teleporter who submitted the request 42 | /// @param _arbitraryData Data written in Bitcoin tx 43 | /// @return parsedValue The parsed value of percentage fee 44 | function parsePercentageFee(bytes memory _arbitraryData) internal pure returns (uint16 parsedValue) { 45 | bytes memory slicedBytes = sliceBytes(_arbitraryData, 23, 24); 46 | assembly { 47 | parsedValue := mload(add(slicedBytes, 2)) 48 | } 49 | } 50 | 51 | /// @notice Returns speed of request 52 | /// @dev 0 for normal requests, 1 for instant requests 53 | /// Instant requests are used to pay back an instant loan 54 | /// @param _arbitraryData Data written in Bitcoin tx 55 | /// @return parsedValue The parsed value of speed parameter 56 | function parseSpeed(bytes memory _arbitraryData) internal pure returns (uint8 parsedValue) { 57 | bytes memory slicedBytes = sliceBytes(_arbitraryData, 25, 25); 58 | assembly { 59 | parsedValue := mload(add(slicedBytes, 1)) 60 | } 61 | } 62 | 63 | /// @notice Returns address of exchange token 64 | /// @dev Minted TeleBTC will be exchanged to this token 65 | /// @param _arbitraryData Data written in Bitcoin tx 66 | /// @return parsedValue The parsed value of exchange token 67 | function parseExchangeToken(bytes memory _arbitraryData) internal pure returns (address parsedValue){ 68 | bytes memory slicedBytes = sliceBytes(_arbitraryData, 26, 45); 69 | assembly { 70 | parsedValue := mload(add(slicedBytes, 20)) 71 | } 72 | } 73 | 74 | /// @notice Returns amount of output (exchange) token 75 | /// @dev If input token is fixed, outputAmount means the min expected output amount 76 | /// If output token is fixed, outputAmount is desired output amount 77 | /// @param _arbitraryData Data written in Bitcoin tx 78 | /// @return parsedValue The parsed value of exchange output amount 79 | function parseExchangeOutputAmount(bytes memory _arbitraryData) internal pure returns (uint224 parsedValue){ 80 | bytes memory slicedBytes = sliceBytes(_arbitraryData, 46, 73); 81 | assembly { 82 | parsedValue := mload(add(slicedBytes, 28)) 83 | } 84 | } 85 | 86 | /// @notice Returns deadline of executing exchange request 87 | /// @dev This value is compared to block.timestamp 88 | /// @param _arbitraryData Data written in Bitcoin tx 89 | /// @return parsedValue The parsed value of deadline 90 | function parseDeadline(bytes memory _arbitraryData) internal pure returns (uint32 parsedValue){ 91 | bytes memory slicedBytes = sliceBytes(_arbitraryData, 74, 77); 92 | assembly { 93 | parsedValue := mload(add(slicedBytes, 4)) 94 | } 95 | } 96 | 97 | /// @notice Returns true if input token is fixed 98 | /// @param _arbitraryData Data written in Bitcoin tx 99 | /// @return parsedValue The parsed value of is-fixed-token 100 | function parseIsFixedToken(bytes memory _arbitraryData) internal pure returns (uint8 parsedValue){ 101 | bytes memory slicedBytes = sliceBytes(_arbitraryData, 78, 78); 102 | assembly { 103 | parsedValue := mload(add(slicedBytes, 1)) 104 | } 105 | } 106 | 107 | /// @notice Returns a sliced bytes 108 | /// @param _data Data that is sliced 109 | /// @param _start Start index of slicing 110 | /// @param _end End index of slicing 111 | /// @return _result The result of slicing 112 | function sliceBytes( 113 | bytes memory _data, 114 | uint _start, 115 | uint _end 116 | ) internal pure returns (bytes memory _result) { 117 | bytes1 temp; 118 | for (uint i = _start; i < _end + 1; i++) { 119 | temp = _data[i]; 120 | _result = abi.encodePacked(_result, temp); 121 | } 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /contracts/routers/interfaces/IBurnRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "@teleportdao/btc-evm-bridge/contracts/types/ScriptTypesEnum.sol"; 5 | 6 | interface IBurnRouter { 7 | 8 | // Events 9 | 10 | /// @notice Emits when a burn request gets submitted 11 | /// @param userTargetAddress Address of the user 12 | /// @param userScript Script of user on Bitcoin 13 | /// @param scriptType Script type of the user (for bitcoin address) 14 | /// @param inputAmount Amount of input token (0 if input token is teleBTC) 15 | /// @param inputToken Address of token that will be exchanged for teleBTC (address(0) if input token is teleBTC) 16 | /// @param teleBTCAmount amount of teleBTC that user sent OR Amount of teleBTC after exchanging 17 | /// @param burntAmount that user will receive (after reducing fees) 18 | /// @param lockerTargetAddress Address of Locker 19 | /// @param requestIdOfLocker Index of request between Locker's burn requests 20 | /// @param deadline of Locker for executing the request (in terms of Bitcoin blocks) 21 | event CCBurn( 22 | address indexed userTargetAddress, 23 | bytes userScript, 24 | ScriptTypes scriptType, 25 | uint inputAmount, 26 | address inputToken, 27 | uint teleBTCAmount, 28 | uint burntAmount, 29 | address lockerTargetAddress, 30 | uint requestIdOfLocker, 31 | uint indexed deadline 32 | ); 33 | 34 | /// @notice Emits when a burn proof is provided 35 | /// @param lockerTargetAddress Address of Locker 36 | /// @param requestIdOfLocker Index of paid request of among Locker's requests 37 | /// @param bitcoinTxId The hash of tx that paid a burn request 38 | /// @param bitcoinTxOutputIndex The output index in tx 39 | event PaidCCBurn( 40 | address indexed lockerTargetAddress, 41 | uint requestIdOfLocker, 42 | bytes32 bitcoinTxId, 43 | uint bitcoinTxOutputIndex 44 | ); 45 | 46 | /// @notice Emits when a locker gets slashed for withdrawing BTC without proper reason 47 | /// @param _lockerTargetAddress Locker's address on the target chain 48 | /// @param _blockNumber Block number of the malicious tx 49 | /// @param txId Transaction ID of the malicious tx 50 | /// @param amount Slashed amount 51 | event LockerDispute( 52 | address _lockerTargetAddress, 53 | bytes lockerLockingScript, 54 | uint _blockNumber, 55 | bytes32 txId, 56 | uint amount 57 | ); 58 | 59 | event BurnDispute( 60 | address indexed userTargetAddress, 61 | address indexed _lockerTargetAddress, 62 | bytes lockerLockingScript, 63 | uint requestIdOfLocker 64 | ); 65 | 66 | /// @notice Emits when relay address is updated 67 | event NewRelay( 68 | address oldRelay, 69 | address newRelay 70 | ); 71 | 72 | /// @notice Emits when treasury address is updated 73 | event NewTreasury( 74 | address oldTreasury, 75 | address newTreasury 76 | ); 77 | 78 | /// @notice Emits when lockers address is updated 79 | event NewLockers( 80 | address oldLockers, 81 | address newLockers 82 | ); 83 | 84 | /// @notice Emits when TeleBTC address is updated 85 | event NewTeleBTC( 86 | address oldTeleBTC, 87 | address newTeleBTC 88 | ); 89 | 90 | /// @notice Emits when transfer deadline is updated 91 | event NewTransferDeadline( 92 | uint oldTransferDeadline, 93 | uint newTransferDeadline 94 | ); 95 | 96 | /// @notice Emits when percentage fee is updated 97 | event NewProtocolPercentageFee( 98 | uint oldProtocolPercentageFee, 99 | uint newProtocolPercentageFee 100 | ); 101 | 102 | /// @notice Emits when slasher percentage fee is updated 103 | event NewSlasherPercentageFee( 104 | uint oldSlasherPercentageFee, 105 | uint newSlasherPercentageFee 106 | ); 107 | 108 | /// @notice Emits when bitcoin fee is updated 109 | event NewBitcoinFee( 110 | uint oldBitcoinFee, 111 | uint newBitcoinFee 112 | ); 113 | 114 | /// @notice Emits when bitcoin fee oracle is updated 115 | event NewBitcoinFeeOracle( 116 | address oldBitcoinFeeOracle, 117 | address newBitcoinFeeOracle 118 | ); 119 | 120 | // Read-only functions 121 | 122 | function isTransferred(address _lockerTargetAddress, uint _index) external view returns (bool); 123 | 124 | // State-changing functions 125 | 126 | function setStartingBlockNumber(uint _startingBlockNumber) external; 127 | 128 | function setRelay(address _relay) external; 129 | 130 | function setLockers(address _lockers) external; 131 | 132 | function setTeleBTC(address _teleBTC) external; 133 | 134 | function setTreasury(address _treasury) external; 135 | 136 | function setTransferDeadline(uint _transferDeadline) external; 137 | 138 | function setProtocolPercentageFee(uint _protocolPercentageFee) external; 139 | 140 | function setSlasherPercentageReward(uint _slasherPercentageReward) external; 141 | 142 | function setBitcoinFee(uint _bitcoinFee) external; 143 | 144 | function setBitcoinFeeOracle(address _bitcoinFeeOracle) external; 145 | 146 | function ccBurn( 147 | uint _amount, 148 | bytes calldata _userScript, 149 | ScriptTypes _scriptType, 150 | bytes calldata _lockerLockingScript 151 | ) external returns (uint); 152 | 153 | function ccExchangeAndBurn( 154 | address _exchangeConnector, 155 | uint[] calldata _amounts, 156 | bool _isFixedToken, 157 | address[] calldata _path, 158 | uint256 _deadline, 159 | bytes memory _userScript, 160 | ScriptTypes _scriptType, 161 | bytes calldata _lockerLockingScript 162 | ) external returns (uint); 163 | 164 | function burnProof( 165 | bytes4 _version, 166 | bytes memory _vin, 167 | bytes memory _vout, 168 | bytes4 _locktime, 169 | uint256 _blockNumber, 170 | bytes memory _intermediateNodes, 171 | uint _index, 172 | bytes memory _lockerLockingScript, 173 | uint[] memory _burnReqIndexes, 174 | uint[] memory _voutIndexes 175 | ) external payable returns (bool); 176 | 177 | function disputeBurn( 178 | bytes calldata _lockerLockingScript, 179 | uint[] memory _indices 180 | ) external; 181 | 182 | function disputeLocker( 183 | bytes memory _lockerLockingScript, 184 | bytes4[] memory _versions, // [inputTxVersion, outputTxVersion] 185 | bytes memory _inputVin, 186 | bytes memory _inputVout, 187 | bytes memory _outputVin, 188 | bytes memory _outputVout, 189 | bytes4[] memory _locktimes, // [inputTxLocktime, outputTxLocktime] 190 | bytes memory _inputIntermediateNodes, 191 | uint[] memory _indexesAndBlockNumbers 192 | // ^ [inputIndex, inputTxIndex, outputTxIndex, inputTxBlockNumber, outputTxBlockNumber] 193 | ) external payable; 194 | } -------------------------------------------------------------------------------- /contracts/libraries/BurnRouterLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "@teleportdao/btc-evm-bridge/contracts/relay/interfaces/IBitcoinRelay.sol"; 5 | import "@teleportdao/btc-evm-bridge/contracts/libraries/BitcoinHelper.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 7 | import "@openzeppelin/contracts/utils/Address.sol"; 8 | import "../routers/BurnRouterStorage.sol"; 9 | 10 | library BurnRouterLib { 11 | 12 | /// @notice Checks if all outputs of the transaction used to pay a cc burn request 13 | /// @dev One output might return the remaining value to the locker 14 | /// @param _paidOutputCounter Number of the tx outputs that pay a cc burn request 15 | /// @param _vout Outputs of a transaction 16 | /// @param _lockerLockingScript Locking script of locker 17 | /// @param _txId Transaction id 18 | function updateIsUsedAsBurnProof( 19 | mapping(bytes32 => bool) storage _isUsedAsBurnProof, 20 | uint _paidOutputCounter, 21 | bytes memory _vout, 22 | bytes memory _lockerLockingScript, 23 | bytes32 _txId 24 | ) external { 25 | uint parsedAmount = BitcoinHelper.parseValueHavingLockingScript(_vout, _lockerLockingScript); 26 | uint numberOfOutputs = BitcoinHelper.numberOfOutputs(_vout); 27 | 28 | if (parsedAmount != 0 && _paidOutputCounter + 1 == numberOfOutputs) { 29 | // One output sends the remaining value to locker 30 | _isUsedAsBurnProof[_txId] = true; 31 | } else if (_paidOutputCounter == numberOfOutputs) { 32 | // All output pays cc burn requests 33 | _isUsedAsBurnProof[_txId] = true; 34 | } 35 | } 36 | 37 | function disputeBurnHelper( 38 | mapping(address => BurnRouterStorage.burnRequest[]) storage burnRequests, 39 | address _lockerTargetAddress, 40 | uint _index, 41 | uint _transferDeadline, 42 | uint _lastSubmittedHeight, 43 | uint _startingBlockNumber 44 | ) external { 45 | // Checks that locker has not provided burn proof 46 | require( 47 | !burnRequests[_lockerTargetAddress][_index].isTransferred, 48 | "BurnRouterLogic: already paid" 49 | ); 50 | 51 | // Checks that payback deadline has passed 52 | require( 53 | burnRequests[_lockerTargetAddress][_index].deadline < _lastSubmittedHeight, 54 | "BurnRouterLogic: deadline not passed" 55 | ); 56 | 57 | require( 58 | burnRequests[_lockerTargetAddress][_index].deadline > _startingBlockNumber + _transferDeadline, 59 | "BurnRouterLogic: old request" 60 | ); 61 | 62 | // Sets "isTransferred = true" to prevent slashing the locker again 63 | burnRequests[_lockerTargetAddress][_index].isTransferred = true; 64 | } 65 | 66 | function disputeLockerHelper( 67 | mapping(bytes32 => bool) storage _isUsedAsBurnProof, 68 | uint _transferDeadline, 69 | address _relay, 70 | uint _startingBlockNumber, 71 | bytes32 _inputTxId, 72 | bytes4[] memory _versions, // [inputTxVersion, outputTxVersion] 73 | bytes4[] memory _locktimes, // [inputTxLocktime, outputTxLocktime] 74 | bytes memory _inputIntermediateNodes, 75 | uint[] memory _indexesAndBlockNumbers // [inputIndex, inputTxIndex, inputTxBlockNumber] 76 | ) external { 77 | 78 | // Checks input array sizes 79 | require( 80 | _versions.length == 2 && 81 | _locktimes.length == 2 && 82 | _indexesAndBlockNumbers.length == 3, 83 | "BurnRouterLogic: wrong inputs" 84 | ); 85 | 86 | require(_indexesAndBlockNumbers[2] >= _startingBlockNumber, "BurnRouterLogic: old request"); 87 | 88 | require( 89 | isConfirmed( 90 | _relay, 91 | _inputTxId, 92 | _indexesAndBlockNumbers[2], // Block number 93 | _inputIntermediateNodes, 94 | _indexesAndBlockNumbers[1] // Index of input tx in the block 95 | ), 96 | "BurnRouterLogic: not finalized" 97 | ); 98 | 99 | /* 100 | Checks that input tx has not been provided as a burn proof 101 | note: if a locker executes a cc burn request but doesn't provide burn proof before deadline, 102 | we consider the transaction as a malicious tx 103 | */ 104 | require( 105 | !_isUsedAsBurnProof[_inputTxId], 106 | "BurnRouterLogic: already used" 107 | ); 108 | 109 | // prevents multiple slashing of locker 110 | _isUsedAsBurnProof[_inputTxId] = true; 111 | 112 | // Checks that deadline for using the tx as burn proof has passed 113 | require( 114 | lastSubmittedHeight(_relay) > _transferDeadline + _indexesAndBlockNumbers[2], 115 | "BurnRouterLogic: deadline not passed" 116 | ); 117 | 118 | } 119 | 120 | /// @notice Checks inclusion of the transaction in the specified block 121 | /// @dev Calls the relay contract to check Merkle inclusion proof 122 | /// @param _relay Address of Relay contract 123 | /// @param _txId of the transaction 124 | /// @param _blockNumber Height of the block containing the transaction 125 | /// @param _intermediateNodes Merkle inclusion proof for the transaction 126 | /// @param _index Index of transaction in the block 127 | /// @return True if the transaction was included in the block 128 | function isConfirmed( 129 | address _relay, 130 | bytes32 _txId, 131 | uint256 _blockNumber, 132 | bytes memory _intermediateNodes, 133 | uint _index 134 | ) public returns (bool) { 135 | // Finds fee amount 136 | uint feeAmount = getFinalizedBlockHeaderFee(_relay, _blockNumber); 137 | require(msg.value >= feeAmount, "BitcoinRelay: low fee"); 138 | 139 | // Calls relay contract 140 | bytes memory data = Address.functionCallWithValue( 141 | _relay, 142 | abi.encodeWithSignature( 143 | "checkTxProof(bytes32,uint256,bytes,uint256)", 144 | _txId, 145 | _blockNumber, 146 | _intermediateNodes, 147 | _index 148 | ), 149 | feeAmount 150 | ); 151 | 152 | // Sends extra ETH back to msg.sender 153 | Address.sendValue(payable(msg.sender), msg.value - feeAmount); 154 | 155 | return abi.decode(data, (bool)); 156 | } 157 | 158 | function lastSubmittedHeight(address _relay) public view returns (uint) { 159 | return IBitcoinRelay(_relay).lastSubmittedHeight(); 160 | } 161 | 162 | function finalizationParameter(address _relay) external view returns (uint) { 163 | return IBitcoinRelay(_relay).finalizationParameter(); 164 | } 165 | 166 | function getFinalizedBlockHeaderFee(address _relay, uint _blockNumber) public view returns (uint) { 167 | return IBitcoinRelay(_relay).getBlockHeaderFee(_blockNumber, 0); 168 | } 169 | } -------------------------------------------------------------------------------- /test/test_fixtures/ccTransferRequests.json: -------------------------------------------------------------------------------- 1 | { 2 | "normalCCTransfer": { 3 | "txId": "0x1130749bc5d0ff46388cfdaf0b27cb24938dbbc1c25165dd852594139aeb102c", 4 | "version": "0x02000000", 5 | "vin": "0x0168cc0ef5ffb948080a79d85b5ffda89092921e2d776f7eb864cbbbea4271e2a90200000000feffffff", 6 | "vout": "0x03102700000000000017a9144062c8aeed4f81c2d73ff854a2957021191e20b68700000000000000001c6a1a01000082492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 7 | "opReturn": "01000082492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400", 8 | "locktime": "0x00000000", 9 | "blockNumber": 497, 10 | "intermediateNodes": "0x545d893c5311b44ea319e27e53d9d7d2725882cb194a782be75c4da6c71ce0f8", 11 | "index": 1, 12 | "bitcoinAmount": 10000, 13 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 14 | "teleporterFee": 100, 15 | "speed": 0, 16 | "desiredRecipient": "0x4062c8aeed4f81c2d73ff854a2957021191e20b6" 17 | }, 18 | "normalCCTransfer_zeroFee": { 19 | "txId": "0x62286179273c5ee1718cea9f2632b820f810195aa03fb6af321ba5b643d8206d", 20 | "version": "0x02000000", 21 | "vin": "0x0168cc0ef5ffb948080a79d85b5ffda89092921e2d776f7eb864cbbbea4271e2a90200000000feffffff", 22 | "vout": "0x03102700000000000017a9144062c8aeed4f81c2d73ff854a2957021191e20b68700000000000000001c6a1a01000082492cAFDD0BA0F68dec07Da75C28Fdb9d07447d000000533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 23 | "opReturn": "01000082492cAFDD0BA0F68dec07Da75C28Fdb9d07447d000000", 24 | "locktime": "0x00000000", 25 | "blockNumber": 497, 26 | "intermediateNodes": "0x545d893c5311b44ea319e27e53d9d7d2725882cb194a782be75c4da6c71ce0f8", 27 | "index": 1, 28 | "bitcoinAmount": 10000, 29 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 30 | "teleporterFee": 0, 31 | "speed": 0, 32 | "desiredRecipient": "0x4062c8aeed4f81c2d73ff854a2957021191e20b6" 33 | }, 34 | "normalCCTransfer_invalidFee": { 35 | "txId": "0xf1ae8a09ca38078b2881a9ea5991441d9d71e10b72c4cd2f461d5269a615d4b9", 36 | "version": "0x02000000", 37 | "vin": "0x0168cc0ef5ffb948080a79d85b5ffda89092921e2d776f7eb864cbbbea4271e2a90200000000feffffff", 38 | "vout": "0x03102700000000000017a9144062c8aeed4f81c2d73ff854a2957021191e20b68700000000000000001c6a1a01000082492cAFDD0BA0F68dec07Da75C28Fdb9d07447dffff00533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 39 | "opReturn": "01000082492cAFDD0BA0F68dec07Da75C28Fdb9d07447dffff00", 40 | "locktime": "0x00000000", 41 | "blockNumber": 497, 42 | "intermediateNodes": "0x545d893c5311b44ea319e27e53d9d7d2725882cb194a782be75c4da6c71ce0f8", 43 | "index": 1, 44 | "bitcoinAmount": 10000, 45 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 46 | "teleporterFee": 65535, 47 | "speed": 0, 48 | "desiredRecipient": "0x4062c8aeed4f81c2d73ff854a2957021191e20b6" 49 | }, 50 | "normalCCTransfer_invalidAppId": { 51 | "txId": "0xf1ae8a09ca38078b2881a9ea5991441d9d71e10b72c4cd2f461d5269a615d4b9", 52 | "version": "0x02000000", 53 | "vin": "0x0168cc0ef5ffb948080a79d85b5ffda89092921e2d776f7eb864cbbbea4271e2a90200000000feffffff", 54 | "vout": "0x03102700000000000017a9144062c8aeed4f81c2d73ff854a2957021191e20b68700000000000000001c6a1a01ffff82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 55 | "opReturn": "01ffff82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400", 56 | "locktime": "0x00000000", 57 | "blockNumber": 497, 58 | "intermediateNodes": "0x545d893c5311b44ea319e27e53d9d7d2725882cb194a782be75c4da6c71ce0f8", 59 | "index": 1, 60 | "bitcoinAmount": 10000, 61 | "appId": 65535, 62 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 63 | "teleporterFee": 100, 64 | "speed": 0, 65 | "desiredRecipient": "0x4062c8aeed4f81c2d73ff854a2957021191e20b6" 66 | }, 67 | "normalCCTransfer_invalidChainId": { 68 | "txId": "0xf1ae8a09ca38078b2881a9ea5991441d9d71e10b72c4cd2f461d5269a615d4b9", 69 | "version": "0x02000000", 70 | "vin": "0x0168cc0ef5ffb948080a79d85b5ffda89092921e2d776f7eb864cbbbea4271e2a90200000000feffffff", 71 | "vout": "0x03102700000000000017a9144062c8aeed4f81c2d73ff854a2957021191e20b68700000000000000001c6a1aff000082492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 72 | "opReturn": "11000082492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400", 73 | "locktime": "0x00000000", 74 | "blockNumber": 497, 75 | "intermediateNodes": "0x545d893c5311b44ea319e27e53d9d7d2725882cb194a782be75c4da6c71ce0f8", 76 | "index": 1, 77 | "bitcoinAmount": 10000, 78 | "chainId": 255, 79 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 80 | "teleporterFee": 100, 81 | "speed": 0, 82 | "desiredRecipient": "0x4062c8aeed4f81c2d73ff854a2957021191e20b6" 83 | }, 84 | "normalCCTransfer_invalidSpeed": { 85 | "txId": "0xf1ae8a09ca38078b2881a9ea5991441d9d71e10b72c4cd2f461d5269a615d4b9", 86 | "version": "0x02000000", 87 | "vin": "0x0168cc0ef5ffb948080a79d85b5ffda89092921e2d776f7eb864cbbbea4271e2a90200000000feffffff", 88 | "vout": "0x03102700000000000017a9144062c8aeed4f81c2d73ff854a2957021191e20b68700000000000000001c6a1a01000082492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006411533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 89 | "opReturn": "01000082492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400", 90 | "locktime": "0x00000000", 91 | "blockNumber": 497, 92 | "intermediateNodes": "0x545d893c5311b44ea319e27e53d9d7d2725882cb194a782be75c4da6c71ce0f8", 93 | "index": 1, 94 | "bitcoinAmount": 10000, 95 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 96 | "teleporterFee": 100, 97 | "speed": 17, 98 | "desiredRecipient": "0x4062c8aeed4f81c2d73ff854a2957021191e20b6" 99 | }, 100 | "normalCCTransfer_invalidLocker": { 101 | "txId": "0xf1ae8a09ca38078b2881a9ea5991441d9d71e10b72c4cd2f461d5269a615d4b9", 102 | "version": "0x02000000", 103 | "vin": "0x0168cc0ef5ffb948080a79d85b5ffda89092921e2d776f7eb864cbbbea4271e2a90200000000feffffff", 104 | "vout": "0x03102700000000000017a9144062c8aeed4f81c2d73ff854a2957021191e20b58700000000000000001c6a1a01000082492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 105 | "opReturn": "01000082492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400", 106 | "locktime": "0x00000000", 107 | "blockNumber": 497, 108 | "intermediateNodes": "0x545d893c5311b44ea319e27e53d9d7d2725882cb194a782be75c4da6c71ce0f8", 109 | "index": 1, 110 | "bitcoinAmount": 10000, 111 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 112 | "teleporterFee": 100, 113 | "speed": 0, 114 | "desiredRecipient": "0x4062c8aeed4f81c2d73ff854a2957021191e20b5" 115 | }, 116 | "instantCCTransfer": { 117 | "txId": "0xc53aab7159a8dc218f57e5a744eea6c02b7ea30f5942903577c4e0925b3a9647", 118 | "version": "0x02000000", 119 | "vin": "0x0168cc0ef5ffb948080a79d85b5ffda89092921e2d776f7eb864cbbbea4271e2a90200000000feffffff", 120 | "vout": "0x03102700000000000017a9144062c8aeed4f81c2d73ff854a2957021191e20b68700000000000000001c6a1a01000082492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006401533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 121 | "opReturn": "01000082492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006401", 122 | "locktime": "0x00000000", 123 | "blockNumber": 497, 124 | "intermediateNodes": "0x545d893c5311b44ea319e27e53d9d7d2725882cb194a782be75c4da6c71ce0f8", 125 | "index": 1, 126 | "bitcoinAmount": 10000, 127 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 128 | "teleporterFee": 1, 129 | "speed": 1, 130 | "desiredRecipient": "0x4062c8aeed4f81c2d73ff854a2957021191e20b6" 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /contracts/lockers/interfaces/ILockers.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "./ILockersStorage.sol"; 5 | 6 | interface ILockers is ILockersStorage { 7 | 8 | // Events 9 | 10 | event RequestAddLocker( 11 | address indexed lockerTargetAddress, 12 | bytes lockerLockingScript, 13 | uint TDTLockedAmount, 14 | uint nativeTokenLockedAmount 15 | ); 16 | 17 | event RevokeAddLockerRequest( 18 | address indexed lockerTargetAddress, 19 | bytes lockerLockingScript, 20 | uint TDTLockedAmount, 21 | uint nativeTokenLockedAmount 22 | ); 23 | 24 | event RequestInactivateLocker( 25 | address indexed lockerTargetAddress, 26 | uint indexed inactivationTimestamp, 27 | bytes lockerLockingScript, 28 | uint TDTLockedAmount, 29 | uint nativeTokenLockedAmount, 30 | uint netMinted 31 | ); 32 | 33 | event ActivateLocker( 34 | address indexed lockerTargetAddress, 35 | bytes lockerLockingScript, 36 | uint TDTLockedAmount, 37 | uint nativeTokenLockedAmount, 38 | uint netMinted 39 | ); 40 | 41 | event LockerAdded( 42 | address indexed lockerTargetAddress, 43 | bytes lockerLockingScript, 44 | uint TDTLockedAmount, 45 | uint nativeTokenLockedAmount, 46 | uint addingTime 47 | ); 48 | 49 | event LockerRemoved( 50 | address indexed lockerTargetAddress, 51 | bytes lockerLockingScript, 52 | uint TDTUnlockedAmount, 53 | uint nativeTokenUnlockedAmount 54 | ); 55 | 56 | event LockerSlashed( 57 | address indexed lockerTargetAddress, 58 | uint rewardAmount, 59 | address indexed rewardRecipient, 60 | uint amount, 61 | address indexed recipient, 62 | uint slashedCollateralAmount, 63 | uint slashTime, 64 | bool isForCCBurn 65 | ); 66 | 67 | event LockerLiquidated( 68 | address indexed lockerTargetAddress, 69 | address indexed liquidatorAddress, 70 | uint collateralAmount, 71 | uint teleBTCAmount, 72 | uint liquidateTime 73 | ); 74 | 75 | event LockerSlashedCollateralSold( 76 | address indexed lockerTargetAddress, 77 | address indexed buyerAddress, 78 | uint slashingAmount, 79 | uint teleBTCAmount, 80 | uint slashingTime 81 | ); 82 | 83 | event CollateralAdded( 84 | address indexed lockerTargetAddress, 85 | uint addedCollateral, 86 | uint totalCollateral, 87 | uint addingTime 88 | ); 89 | 90 | event CollateralRemoved( 91 | address indexed lockerTargetAddress, 92 | uint removedCollateral, 93 | uint totalCollateral, 94 | uint removingTime 95 | ); 96 | 97 | event MintByLocker( 98 | address indexed lockerTargetAddress, 99 | address indexed receiver, 100 | uint mintedAmount, 101 | uint lockerFee, 102 | uint mintingTime 103 | ); 104 | 105 | event BurnByLocker( 106 | address indexed lockerTargetAddress, 107 | uint burntAmount, 108 | uint lockerFee, 109 | uint burningTime 110 | ); 111 | 112 | event MinterAdded( 113 | address indexed account 114 | ); 115 | 116 | event MinterRemoved( 117 | address indexed account 118 | ); 119 | 120 | event BurnerAdded( 121 | address indexed account 122 | ); 123 | 124 | event BurnerRemoved( 125 | address indexed account 126 | ); 127 | 128 | event NewLockerPercentageFee( 129 | uint oldLockerPercentageFee, 130 | uint newLockerPercentageFee 131 | ); 132 | 133 | event NewPriceWithDiscountRatio( 134 | uint oldPriceWithDiscountRatio, 135 | uint newPriceWithDiscountRatio 136 | ); 137 | 138 | event NewMinRequiredTDTLockedAmount( 139 | uint oldMinRequiredTDTLockedAmount, 140 | uint newMinRequiredTDTLockedAmount 141 | ); 142 | 143 | event NewMinRequiredTNTLockedAmount( 144 | uint oldMinRequiredTNTLockedAmount, 145 | uint newMinRequiredTNTLockedAmount 146 | ); 147 | 148 | event NewPriceOracle( 149 | address oldPriceOracle, 150 | address newPriceOracle 151 | ); 152 | 153 | event NewCCBurnRouter( 154 | address oldCCBurnRouter, 155 | address newCCBurnRouter 156 | ); 157 | 158 | event NewExchangeConnector( 159 | address oldExchangeConnector, 160 | address newExchangeConnector 161 | ); 162 | 163 | event NewTeleportDAOToken( 164 | address oldTDTToken, 165 | address newTDTToken 166 | ); 167 | 168 | event NewTeleBTC( 169 | address oldTeleBTC, 170 | address newTeleBTC 171 | ); 172 | 173 | event NewCollateralRatio( 174 | uint oldCollateralRatio, 175 | uint newCollateralRatio 176 | ); 177 | 178 | event NewLiquidationRatio( 179 | uint oldLiquidationRatio, 180 | uint newLiquidationRatio 181 | ); 182 | 183 | // Read-only functions 184 | 185 | function getLockerTargetAddress(bytes calldata _lockerLockingScript) external view returns (address); 186 | 187 | function isLocker(bytes calldata _lockerLockingScript) external view returns (bool); 188 | 189 | function getNumberOfLockers() external view returns (uint); 190 | 191 | function getLockerLockingScript(address _lockerTargetAddress) external view returns (bytes memory); 192 | 193 | function isLockerActive(address _lockerTargetAddress) external view returns (bool); 194 | 195 | function getLockerCapacity(address _lockerTargetAddress) external view returns (uint); 196 | 197 | function priceOfOneUnitOfCollateralInBTC() external view returns (uint); 198 | 199 | function isMinter(address account) external view returns(bool); 200 | 201 | function isBurner(address account) external view returns(bool); 202 | 203 | // State-changing functions 204 | 205 | function pauseLocker() external; 206 | 207 | function unPauseLocker() external; 208 | 209 | function addMinter(address _account) external; 210 | 211 | function removeMinter(address _account) external; 212 | 213 | function addBurner(address _account) external; 214 | 215 | function removeBurner(address _account) external; 216 | 217 | function mint(bytes calldata _lockerLockingScript, address _receiver, uint _amount) external returns(uint); 218 | 219 | function burn(bytes calldata _lockerLockingScript, uint256 _amount) external returns(uint); 220 | 221 | function setTeleportDAOToken(address _tdtTokenAddress) external; 222 | 223 | function setLockerPercentageFee(uint _lockerPercentageFee) external; 224 | 225 | function setPriceWithDiscountRatio(uint _priceWithDiscountRatio) external; 226 | 227 | function setMinRequiredTDTLockedAmount(uint _minRequiredTDTLockedAmount) external; 228 | 229 | function setMinRequiredTNTLockedAmount(uint _minRequiredTNTLockedAmount) external; 230 | 231 | function setPriceOracle(address _priceOracle) external; 232 | 233 | function setCCBurnRouter(address _ccBurnRouter) external; 234 | 235 | function setExchangeConnector(address _exchangeConnector) external; 236 | 237 | function setTeleBTC(address _teleBTC) external; 238 | 239 | function setCollateralRatio(uint _collateralRatio) external; 240 | 241 | function setLiquidationRatio(uint _liquidationRatio) external; 242 | 243 | function liquidateLocker( 244 | address _lockerTargetAddress, 245 | uint _btcAmount 246 | ) external returns (bool); 247 | 248 | function addCollateral( 249 | address _lockerTargetAddress, 250 | uint _addingNativeTokenAmount 251 | ) external payable returns (bool); 252 | 253 | function removeCollateral( 254 | uint _removingNativeTokenAmount 255 | ) external payable returns (bool); 256 | 257 | function requestToBecomeLocker( 258 | bytes calldata _lockerLockingScript, 259 | uint _lockedTDTAmount, 260 | uint _lockedNativeTokenAmount, 261 | ScriptTypes _lockerRescueType, 262 | bytes calldata _lockerRescueScript 263 | ) external payable returns (bool); 264 | 265 | function revokeRequest() external returns (bool); 266 | 267 | function addLocker(address _lockerTargetAddress) external returns (bool); 268 | 269 | function requestInactivation() external returns (bool); 270 | 271 | function requestActivation() external returns (bool); 272 | 273 | function selfRemoveLocker() external returns (bool); 274 | 275 | function slashIdleLocker( 276 | address _lockerTargetAddress, 277 | uint _rewardAmount, 278 | address _rewardRecipient, 279 | uint _amount, 280 | address _recipient 281 | ) external returns(bool); 282 | 283 | function slashThiefLocker( 284 | address _lockerTargetAddress, 285 | uint _rewardAmount, 286 | address _rewardRecipient, 287 | uint _amount 288 | ) external returns(bool); 289 | 290 | function buySlashedCollateralOfLocker( 291 | address _lockerTargetAddress, 292 | uint _collateralAmount 293 | ) external returns (bool); 294 | 295 | } -------------------------------------------------------------------------------- /test/telebtc.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { deployments, ethers } from "hardhat"; 3 | import { Signer, BigNumber } from "ethers"; 4 | import { Address } from "hardhat-deploy/types"; 5 | import {TeleBTC} from "../src/types/TeleBTC"; 6 | import {TeleBTC__factory} from "../src/types/factories/TeleBTC__factory"; 7 | import { network } from "hardhat" 8 | 9 | 10 | describe("TeleBTC", async () => { 11 | 12 | // Constants 13 | const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"; 14 | const ONE_ADDRESS = "0x0000000000000000000000000000000000000011"; 15 | const maxMintLimit = 10 ** 8; 16 | const epochLength = 2000; 17 | 18 | // Accounts 19 | let deployer: Signer; 20 | let signer1: Signer; 21 | let signer2: Signer; 22 | let deployerAddress: Address; 23 | let signer1Address: Address; 24 | let signer2Address: Address; 25 | 26 | // Contracts 27 | let teleBTC: TeleBTC; 28 | 29 | 30 | before(async () => { 31 | // Sets accounts 32 | [deployer, signer1, signer2] = await ethers.getSigners(); 33 | deployerAddress = await deployer.getAddress(); 34 | signer1Address = await signer1.getAddress(); 35 | signer2Address = await signer2.getAddress(); 36 | 37 | const teleBTCFactory = new TeleBTC__factory(deployer); 38 | teleBTC = await teleBTCFactory.deploy( 39 | "teleBTC", 40 | "TBTC" 41 | ); 42 | 43 | 44 | await teleBTC.addMinter(signer2Address) 45 | }); 46 | 47 | 48 | describe("#mint rate limit", async () => { 49 | 50 | it("can't mint more than maximum mint limit in one transaction", async function () { 51 | await expect( 52 | teleBTC.connect(signer2).mint(ONE_ADDRESS, maxMintLimit * 2) 53 | ).to.be.revertedWith( 54 | "TeleBTC: mint amount is more than maximum mint limit" 55 | ) 56 | }) 57 | 58 | it("can't mint more than maximum mint limit in one epoch", async function () { 59 | await teleBTC.connect(signer2).mint(ONE_ADDRESS, maxMintLimit - 10) 60 | await expect( 61 | await teleBTC.lastMintLimit() 62 | ).to.be.equal( 63 | 10 64 | ) 65 | await expect( 66 | teleBTC.connect(signer2).mint(ONE_ADDRESS, 11) 67 | ).to.be.revertedWith( 68 | "TeleBTC: reached maximum mint limit" 69 | ) 70 | }) 71 | 72 | it("after an epoch, mint rate limit will be reset", async function () { 73 | await moveBlocks(epochLength) 74 | 75 | await teleBTC.connect(signer2).mint(ONE_ADDRESS, maxMintLimit - 10) 76 | await expect( 77 | await teleBTC.lastMintLimit() 78 | ).to.be.equal( 79 | 10 80 | ) 81 | 82 | await teleBTC.connect(signer2).mint(ONE_ADDRESS, 5) 83 | await expect( 84 | await teleBTC.lastMintLimit() 85 | ).to.be.equal( 86 | 5 87 | ) 88 | 89 | await expect( 90 | teleBTC.connect(signer2).mint(ONE_ADDRESS, 10) 91 | ).to.be.revertedWith( 92 | "TeleBTC: reached maximum mint limit" 93 | ) 94 | 95 | await moveBlocks(epochLength) 96 | await teleBTC.connect(signer2).mint(ONE_ADDRESS, 10) 97 | await expect( 98 | await teleBTC.lastMintLimit() 99 | ).to.be.equal( 100 | maxMintLimit - 10 101 | ) 102 | }) 103 | 104 | async function moveBlocks(amount: number) { 105 | for (let index = 0; index < amount; index++) { 106 | await network.provider.request({ 107 | method: "evm_mine", 108 | params: [], 109 | }) 110 | } 111 | } 112 | 113 | }); 114 | 115 | describe("#burn and mint", async () => { 116 | 117 | it("non burner account can't burn tokens", async function () { 118 | await expect( 119 | teleBTC.connect(signer2).burn(10) 120 | ).to.be.revertedWith( 121 | "TeleBTC: only burners can burn" 122 | ) 123 | }) 124 | 125 | it("non minter account can't mint tokens", async function () { 126 | await expect( 127 | teleBTC.connect(deployer).mint(deployerAddress, 10) 128 | ).to.be.revertedWith( 129 | "TeleBTC: only minters can mint" 130 | ) 131 | }) 132 | 133 | it("minters can mint tokens and burner can burn tokens", async function () { 134 | await teleBTC.addBurner(signer2Address) 135 | await teleBTC.connect(signer2).mint(signer2Address, 10) 136 | await expect( 137 | teleBTC.connect(signer2).burn(10) 138 | ).to.emit( 139 | teleBTC, "Burn" 140 | ).withArgs(signer2Address, signer2Address, 10); 141 | }) 142 | 143 | }); 144 | 145 | describe("#minter", async () => { 146 | 147 | it("add minter", async function () { 148 | await expect( 149 | await teleBTC.addMinter(ONE_ADDRESS) 150 | ).to.emit( 151 | teleBTC, "MinterAdded" 152 | ).withArgs(ONE_ADDRESS); 153 | }) 154 | 155 | it("can't add zero address as minter", async function () { 156 | await expect( 157 | teleBTC.addMinter(ZERO_ADDRESS) 158 | ).to.be.revertedWith( 159 | "TeleBTC: account is the zero address" 160 | ) 161 | }) 162 | 163 | it("can't add minter twice", async function () { 164 | await expect( 165 | teleBTC.addMinter(ONE_ADDRESS) 166 | ).to.be.revertedWith( 167 | "TeleBTC: account already has role" 168 | ) 169 | }) 170 | 171 | it("remove minter", async function () { 172 | await expect( 173 | await teleBTC.removeMinter(ONE_ADDRESS) 174 | ).to.emit( 175 | teleBTC, "MinterRemoved" 176 | ).withArgs(ONE_ADDRESS); 177 | }) 178 | 179 | }); 180 | 181 | describe("#burner", async () => { 182 | 183 | it("add burner", async function () { 184 | await expect( 185 | await teleBTC.addBurner(ONE_ADDRESS) 186 | ).to.emit( 187 | teleBTC, "BurnerAdded" 188 | ).withArgs(ONE_ADDRESS); 189 | }) 190 | 191 | it("can't add zero address as burner", async function () { 192 | await expect( 193 | teleBTC.addBurner(ZERO_ADDRESS) 194 | ).to.be.revertedWith( 195 | "TeleBTC: account is the zero address" 196 | ) 197 | }) 198 | 199 | it("can't add burner twice", async function () { 200 | await expect( 201 | teleBTC.addBurner(ONE_ADDRESS) 202 | ).to.be.revertedWith( 203 | "TeleBTC: account already has role" 204 | ) 205 | }) 206 | 207 | it("remove burner", async function () { 208 | await expect( 209 | await teleBTC.removeBurner(ONE_ADDRESS) 210 | ).to.emit( 211 | teleBTC, "BurnerRemoved" 212 | ).withArgs(ONE_ADDRESS); 213 | }) 214 | 215 | }); 216 | 217 | describe("Renounce ownership", async () => { 218 | it("owner can't renounce his ownership", async function () { 219 | await teleBTC.renounceOwnership() 220 | await expect( 221 | await teleBTC.owner() 222 | ).to.be.equal(deployerAddress) 223 | }) 224 | }) 225 | 226 | describe("Setters", async () => { 227 | 228 | it("none owner accounts can't change maximum mint limit", async function () { 229 | await expect( 230 | teleBTC.connect(signer1).setMaxMintLimit(10) 231 | ).to.be.revertedWith( 232 | "Ownable: caller is not the owner" 233 | ) 234 | }) 235 | 236 | it("owner account can change maximum mint limit", async function () { 237 | await expect( 238 | await teleBTC.setMaxMintLimit(10) 239 | ).to.emit( 240 | teleBTC, "NewMintLimit" 241 | ).withArgs( 242 | maxMintLimit, 10 243 | ) 244 | 245 | await expect( 246 | await teleBTC.maxMintLimit() 247 | ).to.equal(10) 248 | 249 | }) 250 | 251 | it("none owner accounts can't change epoch length", async function () { 252 | await expect( 253 | teleBTC.connect(signer1).setEpochLength(10) 254 | ).to.be.revertedWith ( 255 | "Ownable: caller is not the owner" 256 | ) 257 | }) 258 | 259 | it("can't change epoch length to zero", async function () { 260 | await expect( 261 | teleBTC.connect(deployer).setEpochLength(0) 262 | ).to.be.revertedWith ( 263 | "TeleBTC: value is zero" 264 | ) 265 | }) 266 | 267 | it("owner account can change epoch length", async function () { 268 | await expect( 269 | await teleBTC.setEpochLength(10) 270 | ).to.emit( 271 | teleBTC, "NewEpochLength" 272 | ).withArgs( 273 | epochLength, 10 274 | ) 275 | 276 | await expect( 277 | await teleBTC.epochLength() 278 | ).to.equal(10) 279 | 280 | }) 281 | 282 | it("can't change epoch length to zero", async function () { 283 | await expect( 284 | teleBTC.setEpochLength(0) 285 | ).to.be.revertedWith( 286 | "TeleBTC: value is zero" 287 | ) 288 | }) 289 | }) 290 | 291 | describe("Getters", async () => { 292 | it("decimal is correct", async function () { 293 | await expect( 294 | await teleBTC.decimals() 295 | ).to.be.equal(8) 296 | }) 297 | }) 298 | 299 | 300 | }) -------------------------------------------------------------------------------- /contracts/erc20/TeleBTCLogic.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "./interfaces/ITeleBTC.sol"; 5 | import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; 6 | import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 7 | import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; 8 | 9 | contract TeleBTCLogic is ITeleBTC, ERC20Upgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable { 10 | 11 | modifier onlyBlackLister() { 12 | require(isBlackLister(_msgSender()), "TeleBTC: only blacklisters"); 13 | _; 14 | } 15 | 16 | modifier notBlackListed(address _account) { 17 | require(!isBlackListed(_account), "TeleBTC: blacklisted"); 18 | _; 19 | } 20 | 21 | modifier onlyMinter() { 22 | require(isMinter(_msgSender()), "TeleBTC: only minters can mint"); 23 | _; 24 | } 25 | 26 | modifier onlyBurner() { 27 | require(isBurner(_msgSender()), "TeleBTC: only burners can burn"); 28 | _; 29 | } 30 | 31 | modifier nonZeroValue(uint _value) { 32 | require(_value > 0, "TeleBTC: value is zero"); 33 | _; 34 | } 35 | 36 | // Public variables 37 | mapping(address => bool) public minters; 38 | mapping(address => bool) public burners; 39 | mapping(address => bool) public blacklisters; 40 | 41 | mapping(address => bool) internal blacklisted; 42 | 43 | uint public maxMintLimit; // Maximum mint limit per epoch 44 | uint public lastMintLimit; // Current mint limit in last epoch, decrease by minting in an epoch 45 | uint public epochLength; // Number of blocks in every epoch 46 | uint public lastEpoch; // Epoch number of last mint transaction 47 | 48 | function initialize( 49 | string memory _name, 50 | string memory _symbol 51 | ) public initializer { 52 | ERC20Upgradeable.__ERC20_init( 53 | _name, 54 | _symbol 55 | ); 56 | OwnableUpgradeable.__Ownable_init(); 57 | ReentrancyGuardUpgradeable.__ReentrancyGuard_init(); 58 | 59 | maxMintLimit = 10 ** 8; 60 | lastMintLimit = 10 ** 8; 61 | epochLength = 2000; 62 | } 63 | 64 | function renounceOwnership() public virtual override onlyOwner {} 65 | 66 | function decimals() public view virtual override(ERC20Upgradeable, ITeleBTC) returns (uint8) { 67 | return 8; 68 | } 69 | 70 | /** 71 | * @dev change maximum mint limit per epoch. 72 | */ 73 | function setMaxMintLimit(uint _mintLimit) public override onlyOwner { 74 | emit NewMintLimit(maxMintLimit, _mintLimit); 75 | maxMintLimit = _mintLimit; 76 | } 77 | 78 | /** 79 | * @dev change blocks number per epoch. 80 | */ 81 | function setEpochLength(uint _length) public override onlyOwner nonZeroValue(_length) { 82 | emit NewEpochLength(epochLength, _length); 83 | epochLength = _length; 84 | } 85 | 86 | /** 87 | * @dev Check if an account is blacklister. 88 | * @return bool 89 | */ 90 | function isBlackLister(address account) internal view returns (bool) { 91 | require(account != address(0), "TeleBTC: zero address"); 92 | return blacklisters[account]; 93 | } 94 | 95 | /** 96 | * @dev Check if an account is blacklisted. 97 | * @return bool 98 | */ 99 | function isBlackListed(address account) public view returns (bool) { 100 | // require(account != address(0), "TeleBTC: zero address"); 101 | return blacklisted[account]; 102 | } 103 | 104 | /** 105 | * @dev Check if an account is minter. 106 | * @return bool 107 | */ 108 | function isMinter(address account) internal view returns (bool) { 109 | require(account != address(0), "TeleBTC: zero address"); 110 | return minters[account]; 111 | } 112 | 113 | /// @notice Check if an account is burner 114 | /// @param account The account which intended to be checked 115 | /// @return bool 116 | function isBurner(address account) internal view returns (bool) { 117 | require(account != address(0), "TeleBTC: zero address"); 118 | return burners[account]; 119 | } 120 | 121 | /// @notice Adds a blacklister 122 | /// @dev Only owner can call this function 123 | /// @param account The account which intended to be added to blacklisters 124 | function addBlackLister(address account) external override onlyOwner { 125 | require(!isBlackLister(account), "TeleBTC: already has role"); 126 | blacklisters[account] = true; 127 | emit BlackListerAdded(account); 128 | } 129 | 130 | /// @notice Removes a blacklister 131 | /// @dev Only owner can call this function 132 | /// @param account The account which intended to be removed from blacklisters 133 | function removeBlackLister(address account) external override onlyOwner { 134 | require(isBlackLister(account), "TeleBTC: does not have role"); 135 | blacklisters[account] = false; 136 | emit BlackListerRemoved(account); 137 | } 138 | 139 | /// @notice Adds a minter 140 | /// @dev Only owner can call this function 141 | /// @param account The account which intended to be added to minters 142 | function addMinter(address account) external override onlyOwner { 143 | require(!isMinter(account), "TeleBTC: already has role"); 144 | minters[account] = true; 145 | emit MinterAdded(account); 146 | } 147 | 148 | /// @notice Removes a minter 149 | /// @dev Only owner can call this function 150 | /// @param account The account which intended to be removed from minters 151 | function removeMinter(address account) external override onlyOwner { 152 | require(isMinter(account), "TeleBTC: does not have role"); 153 | minters[account] = false; 154 | emit MinterRemoved(account); 155 | } 156 | 157 | /// @notice Adds a burner 158 | /// @dev Only owner can call this function 159 | /// @param account The account which intended to be added to burners 160 | function addBurner(address account) external override onlyOwner { 161 | require(!isBurner(account), "TeleBTC: already has role"); 162 | burners[account] = true; 163 | emit BurnerAdded(account); 164 | } 165 | 166 | /// @notice Removes a burner 167 | /// @dev Only owner can call this function 168 | /// @param account The account which intended to be removed from burners 169 | function removeBurner(address account) external override onlyOwner { 170 | require(isBurner(account), "TeleBTC: does not have role"); 171 | burners[account] = false; 172 | emit BurnerRemoved(account); 173 | } 174 | 175 | /// @notice Burns TeleBTC tokens of msg.sender 176 | /// @dev Only burners can call this 177 | /// @param _amount Amount of burnt tokens 178 | function burn(uint _amount) external nonReentrant onlyBurner override returns (bool) { 179 | _burn(_msgSender(), _amount); 180 | emit Burn(_msgSender(), _msgSender(), _amount); 181 | return true; 182 | } 183 | 184 | /// @notice Burns TeleBTC tokens of user 185 | /// @dev Only owner can call this 186 | /// @param _user Address of user whose teleBTC is burnt 187 | /// @param _amount Amount of burnt tokens 188 | function ownerBurn(address _user, uint _amount) external nonReentrant onlyOwner override returns (bool) { 189 | 190 | if (isBlackListed(_user)) { 191 | blacklisted[_user] = false; 192 | _burn(_user, _amount); 193 | blacklisted[_user] = true; 194 | } else { 195 | _burn(_user, _amount); 196 | } 197 | 198 | emit Burn(owner(), _user, _amount); 199 | return true; 200 | } 201 | 202 | /// @notice Mints TeleBTC tokens for _receiver 203 | /// @dev Only minters can call this 204 | /// @param _receiver Address of token's receiver 205 | /// @param _amount Amount of minted tokens 206 | function mint(address _receiver, uint _amount) external nonReentrant onlyMinter override returns (bool) { 207 | require(_amount <= maxMintLimit, "TeleBTC: mint amount is more than maximum mint limit"); 208 | require(checkAndReduceMintLimit(_amount), "TeleBTC: reached maximum mint limit"); 209 | 210 | _mint(_receiver, _amount); 211 | emit Mint(_msgSender(), _receiver, _amount); 212 | return true; 213 | } 214 | 215 | /// @notice Check if can mint new tokens and update mint limit 216 | /// @param _amount Desired mint amount 217 | function checkAndReduceMintLimit(uint _amount) private returns (bool) { 218 | uint currentEpoch = block.number / epochLength; 219 | 220 | if (currentEpoch == lastEpoch) { 221 | if (_amount > lastMintLimit) 222 | return false; 223 | lastMintLimit -= _amount; 224 | } else { 225 | lastEpoch = currentEpoch; 226 | lastMintLimit = maxMintLimit - _amount; 227 | } 228 | return true; 229 | } 230 | 231 | /// @notice Blacklist an account 232 | /// @dev Only Blacklisters can call this 233 | /// @param _account Account blacklisted 234 | function blacklist(address _account) external override nonReentrant onlyBlackLister { 235 | blacklisted[_account] = true; 236 | emit Blacklisted(_account); 237 | } 238 | 239 | /// @notice UnBlacklist an account 240 | /// @dev Only Blacklisters can call this 241 | /// @param _account Account unblacklisted 242 | function unBlacklist(address _account) external override nonReentrant onlyBlackLister { 243 | blacklisted[_account] = false; 244 | emit UnBlacklisted(_account); 245 | } 246 | 247 | function _beforeTokenTransfer( 248 | address from, 249 | address to, 250 | uint256 amount 251 | ) internal view override { 252 | require(!isBlackListed(from), "TeleBTC: from is blacklisted"); 253 | require(!isBlackListed(to), "TeleBTC: to is blacklisted"); 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /scripts/init-config/001-init-contracts.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { DeployFunction } from 'hardhat-deploy/types'; 3 | import { ethers } from "hardhat"; 4 | import config from 'config'; 5 | const logger = require('node-color-log'); 6 | 7 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 8 | const { deployments } = hre; 9 | const ZERO_ADD = "0x0000000000000000000000000000000000000000"; 10 | 11 | const lockersLib = await deployments.get("LockersLib") 12 | const lockersLogic = await deployments.get("LockersLogic") 13 | const teleDAOToken = ZERO_ADD; 14 | const teleBTC = await deployments.get("TeleBTCProxy"); 15 | const teleBTCLogic = await deployments.get("TeleBTCLogic"); 16 | const exchangeConnector = await deployments.get("UniswapV2Connector"); 17 | const priceOracle = await deployments.get("PriceOracle"); 18 | const ccTransferRouterLogic = await deployments.get("CcTransferRouterLogic"); 19 | const ccTransferRouterProxy = await deployments.get("CcTransferRouterProxy"); 20 | const lockersProxy = await deployments.get("LockersProxy"); 21 | const burnRouterLib = await deployments.get("BurnRouterLib"); 22 | const burnRouterLogic = await deployments.get("BurnRouterLogic"); 23 | const burnRouterProxy = await deployments.get("BurnRouterProxy"); 24 | const ccExchangeRouterLogic = await deployments.get("CcExchangeRouterLogic"); 25 | const ccExchangeRouterProxy = await deployments.get("CcExchangeRouterProxy"); 26 | 27 | const minTDTLockedAmount = 0; 28 | const startingBlockHeight = config.get("starting_block_height"); 29 | const protocolPercentageFee = config.get("cc_transfer.protocol_percentage_fee"); 30 | const chainId = config.get("chain_id"); 31 | const appId = config.get("cc_transfer.app_id"); 32 | const treasuryAddress = config.get("treasury"); 33 | const bitcoinRelay = config.get("bitcoin_relay"); 34 | const minNativeLockedAmount = config.get("lockers_contract.minimum_native_locked_amount"); 35 | const collateralRatio = config.get("lockers_contract.collateral_ratio"); 36 | const liquidationRatio = config.get("lockers_contract.liquidation_ratio"); 37 | const lockerPercentageFee = config.get("lockers_contract.locker_percentage_fee"); 38 | const priceWithDiscountRatio = config.get("lockers_contract.price_with_discount_ratio"); 39 | const slasherPercentageReward = config.get("cc_burn.slasher_percentage_reward"); 40 | const bitcoinFee = config.get("cc_burn.bitcoin_fee"); 41 | const transferDeadLine = config.get("cc_burn.transfer_deadLine"); 42 | const chainID = config.get("chain_id"); 43 | 44 | logger.color('blue').log("-------------------------------------------------") 45 | logger.color('blue').bold().log("Initialize Lockers ...") 46 | 47 | const lockersLogicFactory = await ethers.getContractFactory( 48 | "LockersLogic", 49 | { 50 | libraries: { 51 | LockersLib: lockersLib.address 52 | } 53 | } 54 | ); 55 | const lockersProxyInstance = await lockersLogicFactory.attach( 56 | lockersProxy.address 57 | ); 58 | const lockersLogicInstance = await lockersLogicFactory.attach( 59 | lockersLogic.address 60 | ); 61 | 62 | let _teleBtcProxy = await lockersProxyInstance.teleBTC(); 63 | if (_teleBtcProxy == ZERO_ADD) { 64 | const initializeTx = await lockersProxyInstance.initialize( 65 | teleBTC.address, 66 | teleDAOToken, 67 | exchangeConnector.address, 68 | priceOracle.address, 69 | burnRouterProxy.address, 70 | minTDTLockedAmount, 71 | minNativeLockedAmount, 72 | collateralRatio, 73 | liquidationRatio, 74 | lockerPercentageFee, 75 | priceWithDiscountRatio 76 | ) 77 | await initializeTx.wait(1) 78 | console.log("Initialized lockersProxy: ", initializeTx.hash) 79 | } else { 80 | console.log("lockersProxy is already initialized") 81 | } 82 | 83 | let _teleBtcLogic = await lockersLogicInstance.teleBTC() 84 | if (_teleBtcLogic == ZERO_ADD) { 85 | const initializeTx = await lockersLogicInstance.initialize( 86 | teleBTC.address, 87 | teleDAOToken, 88 | exchangeConnector.address, 89 | priceOracle.address, 90 | burnRouterProxy.address, 91 | minTDTLockedAmount, 92 | minNativeLockedAmount, 93 | collateralRatio, 94 | liquidationRatio, 95 | lockerPercentageFee, 96 | priceWithDiscountRatio 97 | ) 98 | await initializeTx.wait(1) 99 | console.log("Initialized lockersLogic: ", initializeTx.hash) 100 | } else { 101 | console.log("lockersLogic is already initialized") 102 | } 103 | 104 | logger.color('blue').log("-------------------------------------------------") 105 | logger.color('blue').bold().log("Initialize CcTransferRouter ...") 106 | 107 | const ccTransferRouterLogicFactory = await ethers.getContractFactory( 108 | "CcTransferRouterLogic" 109 | ); 110 | const ccTransferRouterProxyInstance = await ccTransferRouterLogicFactory.attach( 111 | ccTransferRouterProxy.address 112 | ); 113 | const ccTransferRouterLogicInstance = await ccTransferRouterLogicFactory.attach( 114 | ccTransferRouterLogic.address 115 | ); 116 | 117 | let _relayProxy = await ccTransferRouterProxyInstance.relay(); 118 | if (_relayProxy == ZERO_ADD) { 119 | const initializeTxProxy = await ccTransferRouterProxyInstance.initialize( 120 | startingBlockHeight, 121 | protocolPercentageFee, 122 | chainId, 123 | appId, 124 | bitcoinRelay, 125 | lockersProxy.address, 126 | teleBTC.address, 127 | treasuryAddress 128 | ); 129 | await initializeTxProxy.wait(1); 130 | console.log("Initialized CcTransferRouterProxy: ", initializeTxProxy.hash); 131 | } 132 | 133 | let _relayLogic = await ccTransferRouterLogicInstance.relay(); 134 | if (_relayLogic == ZERO_ADD) { 135 | const initializeTxLogic = await ccTransferRouterLogicInstance.initialize( 136 | startingBlockHeight, 137 | protocolPercentageFee, 138 | chainId, 139 | appId, 140 | bitcoinRelay, 141 | lockersProxy.address, 142 | teleBTC.address, 143 | treasuryAddress 144 | ) 145 | await initializeTxLogic.wait(1); 146 | console.log("Initialized CcTransferRouterLogic: ", initializeTxLogic.hash); 147 | } 148 | 149 | logger.color('blue').log("-------------------------------------------------") 150 | logger.color('blue').bold().log("Initialize BurnRouter ...") 151 | 152 | const burnRouterLogicFactory = await ethers.getContractFactory( 153 | "BurnRouterLogic", 154 | { 155 | libraries: { 156 | BurnRouterLib: burnRouterLib.address 157 | } 158 | } 159 | ); 160 | const burnRouterProxyInstance = await burnRouterLogicFactory.attach( 161 | burnRouterProxy.address 162 | ); 163 | const burnRouterLogicInstance = await burnRouterLogicFactory.attach( 164 | burnRouterLogic.address 165 | ); 166 | 167 | _relayProxy = await burnRouterProxyInstance.relay(); 168 | if (_relayProxy == ZERO_ADD) { 169 | const initializeTxProxy = await burnRouterProxyInstance.initialize( 170 | startingBlockHeight, 171 | bitcoinRelay, 172 | lockersProxy.address, 173 | treasuryAddress, 174 | teleBTC.address, 175 | transferDeadLine, 176 | protocolPercentageFee, 177 | slasherPercentageReward, 178 | bitcoinFee 179 | ) 180 | await initializeTxProxy.wait(1); 181 | console.log("Initialized BurnRouterProxy: ", initializeTxProxy.hash); 182 | } 183 | 184 | _relayLogic = await burnRouterLogicInstance.relay(); 185 | if (_relayLogic == ZERO_ADD) { 186 | const initializeTxLogic = await burnRouterLogicInstance.initialize( 187 | startingBlockHeight, 188 | bitcoinRelay, 189 | lockersProxy.address, 190 | treasuryAddress, 191 | teleBTC.address, 192 | transferDeadLine, 193 | protocolPercentageFee, 194 | slasherPercentageReward, 195 | bitcoinFee 196 | ) 197 | await initializeTxLogic.wait(1); 198 | console.log("Initialized BurnRouterLogic: ", initializeTxLogic.hash); 199 | } 200 | 201 | logger.color('blue').log("-------------------------------------------------") 202 | logger.color('blue').bold().log("Initialize CcExchangeRouter ...") 203 | 204 | const ccExchangeRouterLogicFactory = await ethers.getContractFactory( 205 | "CcExchangeRouterLogic" 206 | ); 207 | const ccExchangeRouterProxyInstance = await ccExchangeRouterLogicFactory.attach( 208 | ccExchangeRouterProxy.address 209 | ); 210 | const ccExchangeRouterLogicInstance = await ccExchangeRouterLogicFactory.attach( 211 | ccExchangeRouterLogic.address 212 | ); 213 | 214 | _relayProxy = await ccExchangeRouterProxyInstance.relay(); 215 | if (_relayProxy == ZERO_ADD) { 216 | const initializeTxProxy = await ccExchangeRouterProxyInstance.initialize( 217 | startingBlockHeight, 218 | protocolPercentageFee, 219 | chainID, 220 | lockersProxy.address, 221 | bitcoinRelay, 222 | teleBTC.address, 223 | treasuryAddress 224 | ); 225 | await initializeTxProxy.wait(1); 226 | console.log("Initialize CcExchangeRouterProxy: ", initializeTxProxy.hash); 227 | } 228 | 229 | _relayLogic = await ccExchangeRouterLogicInstance.relay(); 230 | if (_relayLogic == ZERO_ADD) { 231 | const initializeTxLogic = await ccExchangeRouterLogicInstance.initialize( 232 | startingBlockHeight, 233 | protocolPercentageFee, 234 | chainID, 235 | lockersProxy.address, 236 | bitcoinRelay, 237 | teleBTC.address, 238 | treasuryAddress 239 | ) 240 | await initializeTxLogic.wait(1); 241 | console.log("Initialize CcExchangeRouterLogic: ", initializeTxLogic.hash); 242 | } 243 | 244 | 245 | logger.color('blue').log("-------------------------------------------------") 246 | logger.color('blue').bold().log("Initialize TeleBTC ...") 247 | 248 | const teleBTCLogicFactory = await ethers.getContractFactory( 249 | "TeleBTCLogic" 250 | ); 251 | const teleBTCProxyInstance = await teleBTCLogicFactory.attach( 252 | teleBTC.address 253 | ); 254 | const teleBTCLogicInstance = await teleBTCLogicFactory.attach( 255 | teleBTCLogic.address 256 | ); 257 | 258 | let _ownerProxy = await teleBTCProxyInstance.owner(); 259 | if (_ownerProxy == ZERO_ADD) { 260 | const tokenName = "teleBTC" 261 | const tokenSymbol = "TELEBTC" 262 | 263 | const initializeTxProxy = await teleBTCProxyInstance.initialize( 264 | tokenName, 265 | tokenSymbol 266 | ); 267 | await initializeTxProxy.wait(1); 268 | console.log("Initialize TeleBTC: ", initializeTxProxy.hash); 269 | } 270 | 271 | let _ownerLogic = await teleBTCLogicInstance.owner(); 272 | if (_ownerLogic == ZERO_ADD) { 273 | 274 | const tokenName = "teleBTC" 275 | const tokenSymbol = "TELEBTC" 276 | 277 | const initializeTxLogic = await teleBTCLogicInstance.initialize( 278 | tokenName, 279 | tokenSymbol 280 | ) 281 | await initializeTxLogic.wait(1); 282 | console.log("Initialize TeleBTC: ", initializeTxLogic.hash); 283 | } 284 | 285 | }; 286 | 287 | export default func; -------------------------------------------------------------------------------- /contracts/connectors/UniswapV2Connector.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "./interfaces/IExchangeConnector.sol"; 5 | import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol"; 6 | import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol"; 7 | import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol"; 8 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 9 | import "@openzeppelin/contracts/access/Ownable.sol"; 10 | import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; 11 | 12 | contract UniswapV2Connector is IExchangeConnector, Ownable, ReentrancyGuard { 13 | 14 | 15 | modifier nonZeroAddress(address _address) { 16 | require(_address != address(0), "UniswapV2Connector: zero address"); 17 | _; 18 | } 19 | 20 | string public override name; 21 | address public override exchangeRouter; 22 | address public override liquidityPoolFactory; 23 | address public override wrappedNativeToken; 24 | 25 | /// @notice This contract is used for interacting with UniswapV2 contract 26 | /// @param _name Name of the underlying DEX 27 | /// @param _exchangeRouter Address of the DEX router contract 28 | constructor(string memory _name, address _exchangeRouter) { 29 | name = _name; 30 | exchangeRouter = _exchangeRouter; 31 | liquidityPoolFactory = IUniswapV2Router02(exchangeRouter).factory(); 32 | wrappedNativeToken = IUniswapV2Router02(exchangeRouter).WETH(); 33 | } 34 | 35 | function renounceOwnership() public virtual override onlyOwner {} 36 | 37 | /// @notice Setter for exchange router 38 | /// @dev Gets address of liquidity pool factory from new exchange router 39 | /// @param _exchangeRouter Address of the new exchange router contract 40 | function setExchangeRouter(address _exchangeRouter) external nonZeroAddress(_exchangeRouter) override onlyOwner { 41 | exchangeRouter = _exchangeRouter; 42 | liquidityPoolFactory = IUniswapV2Router02(exchangeRouter).factory(); 43 | wrappedNativeToken = IUniswapV2Router02(exchangeRouter).WETH(); 44 | } 45 | 46 | /// @notice Setter for liquidity pool factory 47 | /// @dev Gets address from exchange router 48 | function setLiquidityPoolFactory() external override onlyOwner { 49 | liquidityPoolFactory = IUniswapV2Router02(exchangeRouter).factory(); 50 | } 51 | 52 | /// @notice Setter for wrapped native token 53 | /// @dev Gets address from exchange router 54 | function setWrappedNativeToken() external override onlyOwner { 55 | wrappedNativeToken = IUniswapV2Router02(exchangeRouter).WETH(); 56 | } 57 | 58 | /// @notice Returns required input amount to get desired output amount 59 | /// @dev Returns (false, 0) if liquidity pool of inputToken-outputToken doesn't exist 60 | /// Returns (false, 0) if desired output amount is greater than or equal to output reserve 61 | /// @param _outputAmount Desired output amount 62 | /// @param _inputToken Address of the input token 63 | /// @param _outputToken Address of the output token 64 | function getInputAmount( 65 | uint _outputAmount, 66 | address _inputToken, 67 | address _outputToken 68 | ) external view nonZeroAddress(_inputToken) nonZeroAddress(_outputToken) override returns (bool, uint) { 69 | 70 | // Checks that the liquidity pool exists 71 | address liquidityPool = IUniswapV2Factory(liquidityPoolFactory).getPair(_inputToken, _outputToken); 72 | 73 | if ( 74 | liquidityPool == address(0) 75 | ) { 76 | if ( 77 | IUniswapV2Factory(liquidityPoolFactory).getPair(_inputToken, wrappedNativeToken) == address(0) || 78 | IUniswapV2Factory(liquidityPoolFactory).getPair(wrappedNativeToken, _outputToken) == address(0) 79 | ) { 80 | return (false, 0); 81 | } 82 | 83 | address[] memory path = new address[](3); 84 | path[0] = _inputToken; 85 | path[1] = wrappedNativeToken; 86 | path[2] = _outputToken; 87 | uint[] memory result = IUniswapV2Router02(exchangeRouter).getAmountsIn(_outputAmount, path); 88 | 89 | return (true, result[0]); 90 | 91 | } else { 92 | 93 | address[] memory path = new address[](2); 94 | path[0] = _inputToken; 95 | path[1] = _outputToken; 96 | uint[] memory result = IUniswapV2Router02(exchangeRouter).getAmountsIn(_outputAmount, path); 97 | 98 | return (true, result[0]); 99 | } 100 | 101 | } 102 | 103 | /// @notice Returns amount of output token that user receives 104 | /// @dev Returns (false, 0) if liquidity pool of inputToken-outputToken doesn't exist 105 | /// @param _inputAmount Amount of input token 106 | /// @param _inputToken Address of the input token 107 | /// @param _outputToken Address of the output token 108 | function getOutputAmount( 109 | uint _inputAmount, 110 | address _inputToken, 111 | address _outputToken 112 | ) external view nonZeroAddress(_inputToken) nonZeroAddress(_outputToken) override returns (bool, uint) { 113 | 114 | // Checks that the liquidity pool exists 115 | address liquidityPool = IUniswapV2Factory(liquidityPoolFactory).getPair(_inputToken, _outputToken); 116 | 117 | if ( 118 | liquidityPool == address(0) 119 | ) { 120 | if ( 121 | IUniswapV2Factory(liquidityPoolFactory).getPair(_inputToken, wrappedNativeToken) == address(0) || 122 | IUniswapV2Factory(liquidityPoolFactory).getPair(wrappedNativeToken, _outputToken) == address(0) 123 | ) { 124 | return (false, 0); 125 | } 126 | 127 | address[] memory path = new address[](3); 128 | path[0] = _inputToken; 129 | path[1] = wrappedNativeToken; 130 | path[2] = _outputToken; 131 | uint[] memory result = IUniswapV2Router02(exchangeRouter).getAmountsOut(_inputAmount, path); 132 | return (true, result[2]); 133 | 134 | } else { 135 | 136 | address[] memory path = new address[](2); 137 | path[0] = _inputToken; 138 | path[1] = _outputToken; 139 | uint[] memory result = IUniswapV2Router02(exchangeRouter).getAmountsOut(_inputAmount, path); 140 | 141 | return (true, result[1]); 142 | } 143 | } 144 | 145 | /// @notice Exchanges input token for output token through exchange router 146 | /// @dev Checks exchange conditions before exchanging 147 | /// We assume that the input token is not WETH (it is teleBTC) 148 | /// @param _inputAmount Amount of input token 149 | /// @param _outputAmount Amount of output token 150 | /// @param _path List of tokens that are used for exchanging 151 | /// @param _to Receiver address 152 | /// @param _deadline Deadline of exchanging tokens 153 | /// @param _isFixedToken True if the input token amount is fixed 154 | /// @return _result True if the exchange is successful 155 | /// @return _amounts Amounts of tokens that are involved in exchanging 156 | function swap( 157 | uint256 _inputAmount, 158 | uint256 _outputAmount, 159 | address[] memory _path, 160 | address _to, 161 | uint256 _deadline, 162 | bool _isFixedToken 163 | ) external nonReentrant nonZeroAddress(_to) override returns (bool _result, uint[] memory _amounts) { 164 | 165 | if (_path.length == 2) { 166 | address liquidityPool = IUniswapV2Factory(liquidityPoolFactory).getPair(_path[0], _path[1]); 167 | 168 | if (liquidityPool == address(0)) { 169 | address[] memory thePath = new address[](3); 170 | 171 | thePath[0] = _path[0]; 172 | thePath[1] = wrappedNativeToken; 173 | thePath[2] = _path[1]; 174 | 175 | _path = thePath; 176 | } 177 | } 178 | 179 | uint neededInputAmount; 180 | (_result, neededInputAmount) = _checkExchangeConditions( 181 | _inputAmount, 182 | _outputAmount, 183 | _path, 184 | _deadline, 185 | _isFixedToken 186 | ); 187 | 188 | if (_result) { 189 | // Gets tokens from user 190 | IERC20(_path[0]).transferFrom(_msgSender(), address(this), neededInputAmount); 191 | 192 | // Gives allowance to exchange router 193 | IERC20(_path[0]).approve(exchangeRouter, neededInputAmount); 194 | 195 | if (_isFixedToken == false && _path[_path.length-1] != wrappedNativeToken) { 196 | _amounts = IUniswapV2Router02(exchangeRouter).swapTokensForExactTokens( 197 | _outputAmount, 198 | _inputAmount, 199 | _path, 200 | _to, 201 | _deadline 202 | ); 203 | } 204 | 205 | if (_isFixedToken == false && _path[_path.length-1] == wrappedNativeToken) { 206 | _amounts = IUniswapV2Router02(exchangeRouter).swapTokensForExactETH( 207 | _outputAmount, 208 | _inputAmount, 209 | _path, 210 | _to, 211 | _deadline 212 | ); 213 | } 214 | 215 | if (_isFixedToken == true && _path[_path.length-1] != wrappedNativeToken) { 216 | _amounts = IUniswapV2Router02(exchangeRouter).swapExactTokensForTokens( 217 | _inputAmount, 218 | _outputAmount, 219 | _path, 220 | _to, 221 | _deadline 222 | ); 223 | } 224 | 225 | if (_isFixedToken == true && _path[_path.length-1] == wrappedNativeToken) { 226 | _amounts = IUniswapV2Router02(exchangeRouter).swapExactTokensForETH( 227 | _inputAmount, 228 | _outputAmount, 229 | _path, 230 | _to, 231 | _deadline 232 | ); 233 | } 234 | emit Swap(_path, _amounts, _to); 235 | } 236 | } 237 | 238 | /// @notice Returns true if the exchange path is valid 239 | /// @param _path List of tokens that are used for exchanging 240 | function isPathValid(address[] memory _path) public view override returns (bool _result) { 241 | address liquidityPool; 242 | 243 | // Checks that path length is greater than one 244 | if (_path.length < 2) { 245 | return false; 246 | } 247 | 248 | for (uint i = 0; i < _path.length - 1; i++) { 249 | liquidityPool = 250 | IUniswapV2Factory(liquidityPoolFactory).getPair(_path[i], _path[i + 1]); 251 | if (liquidityPool == address(0)) { 252 | return false; 253 | } 254 | } 255 | 256 | return true; 257 | } 258 | 259 | /// @notice Checks if exchanging can happen successfully 260 | /// @dev Avoids reverting the execution by exchange router 261 | /// @param _inputAmount Amount of input token 262 | /// @param _outputAmount Amount of output token 263 | /// @param _path List of tokens that are used for exchanging 264 | /// @param _deadline Deadline of exchanging tokens 265 | /// @param _isFixedToken True if the input token amount is fixed 266 | /// @return True if exchange conditions are satisfied 267 | /// @return Needed amount of input token 268 | function _checkExchangeConditions( 269 | uint256 _inputAmount, 270 | uint256 _outputAmount, 271 | address[] memory _path, 272 | uint256 _deadline, 273 | bool _isFixedToken 274 | ) private view returns (bool, uint) { 275 | 276 | // Checks deadline has not passed 277 | if (_deadline < block.timestamp) { 278 | return (false, 0); 279 | } 280 | 281 | // Checks that the liquidity pool exists 282 | if (!isPathValid(_path)) { 283 | return (false, 0); 284 | } 285 | 286 | // Finds maximum output amount 287 | uint[] memory outputResult = IUniswapV2Router02(exchangeRouter).getAmountsOut( 288 | _inputAmount, 289 | _path 290 | ); 291 | 292 | // Checks that exchanging is possible or not 293 | if (_outputAmount > outputResult[_path.length - 1]) { 294 | return (false, 0); 295 | } else { 296 | if (_isFixedToken == true) { 297 | return (true, _inputAmount); 298 | } else { 299 | uint[] memory inputResult = IUniswapV2Router02(exchangeRouter).getAmountsIn( 300 | _outputAmount, 301 | _path 302 | ); 303 | return (true, inputResult[0]); 304 | } 305 | } 306 | } 307 | 308 | } -------------------------------------------------------------------------------- /scripts/init-config/002-set-params.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { DeployFunction } from 'hardhat-deploy/types'; 3 | import { ethers } from "hardhat"; 4 | import config from 'config'; 5 | const logger = require('node-color-log'); 6 | 7 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 8 | const { deployments } = hre; 9 | 10 | const ZERO_ADD = "0x0000000000000000000000000000000000000000"; 11 | 12 | const lockersLib = await deployments.get("LockersLib"); 13 | const lockersProxy = await deployments.get("LockersProxy"); 14 | const teleBTC = await deployments.get("TeleBTCProxy"); 15 | const priceOracle = await deployments.get("PriceOracle"); 16 | const ccTransferRouterProxy = await deployments.get("CcTransferRouterProxy"); 17 | const burnRouterProxy = await deployments.get("BurnRouterProxy"); 18 | const burnRouterLib = await deployments.get("BurnRouterLib"); 19 | const ccExchangeRouterProxy = await deployments.get("CcExchangeRouterProxy"); 20 | const exchangeConnector = await deployments.get("UniswapV2Connector"); 21 | 22 | logger.color('blue').log("-------------------------------------------------") 23 | logger.color('blue').bold().log("Set teleBTC in BurnRouter ...") 24 | 25 | const burnRouterLogicFactory = await ethers.getContractFactory( 26 | "BurnRouterLogic", 27 | { 28 | libraries: { 29 | BurnRouterLib: burnRouterLib.address 30 | } 31 | } 32 | ); 33 | const burnRouterProxyInstance = await burnRouterLogicFactory.attach( 34 | burnRouterProxy.address 35 | ); 36 | 37 | const _teleBTC = await burnRouterProxyInstance.teleBTC() 38 | 39 | if (_teleBTC != teleBTC.address) { 40 | const setTeleBTCTx = await burnRouterProxyInstance.setTeleBTC( 41 | teleBTC.address 42 | ) 43 | await setTeleBTCTx.wait(1) 44 | console.log("Set teleBTC in BurnRouter: ", setTeleBTCTx.hash) 45 | } else { 46 | console.log("teleBTC is already set") 47 | } 48 | 49 | logger.color('blue').log("-------------------------------------------------") 50 | logger.color('blue').bold().log("Set ExchangeConnector in CcExchangeRouterProxy ...") 51 | 52 | const ccExchangeRouterLogicFactory = await ethers.getContractFactory("CcExchangeRouterLogic"); 53 | const ccExchangeRouterProxyInstance = await ccExchangeRouterLogicFactory.attach( 54 | ccExchangeRouterProxy.address 55 | ); 56 | const exchangeAppId = config.get("cc_exchange.app_id") 57 | 58 | const _exchangeConnector = await ccExchangeRouterProxyInstance.exchangeConnector(exchangeAppId) 59 | 60 | if (_exchangeConnector != exchangeConnector.address) { 61 | const setConnectorAndAppIdTx = await ccExchangeRouterProxyInstance.setExchangeConnector( 62 | exchangeAppId, 63 | exchangeConnector.address 64 | ) 65 | await setConnectorAndAppIdTx.wait(1) 66 | console.log("Set ExchangeConnector in CcExchangeRouterProxy: ", setConnectorAndAppIdTx.hash) 67 | } else { 68 | console.log("ExchangeConnector is already set") 69 | } 70 | 71 | logger.color('blue').log("-------------------------------------------------") 72 | logger.color('blue').bold().log("Set teleBTC in Locker...") 73 | 74 | const lockersLogicFactory = await ethers.getContractFactory( 75 | "LockersLogic", 76 | { 77 | libraries: { 78 | LockersLib: lockersLib.address 79 | } 80 | } 81 | ); 82 | const lockersInstance = await lockersLogicFactory.attach( 83 | lockersProxy.address 84 | ); 85 | 86 | logger.color('blue').log("-------------------------------------------------") 87 | logger.color('blue').bold().log("Set teleBTC in Lockers ...") 88 | 89 | if (await lockersInstance.teleBTC() != teleBTC.address) { 90 | const setTeleBTCTx = await lockersInstance.setTeleBTC( 91 | teleBTC.address 92 | ) 93 | await setTeleBTCTx.wait(1) 94 | console.log("Set teleBTC in locker: ", setTeleBTCTx.hash) 95 | } else { 96 | console.log("teleBTC is already set") 97 | } 98 | 99 | const isCCTransferMinter = await lockersInstance.isMinter( 100 | ccTransferRouterProxy.address 101 | ) 102 | 103 | if (!isCCTransferMinter) { 104 | const addCCTransferAsMinter = await lockersInstance.addMinter( 105 | ccTransferRouterProxy.address 106 | ) 107 | await addCCTransferAsMinter.wait(1) 108 | console.log("Added CcTransferRouterProxy as minter: ", addCCTransferAsMinter.hash) 109 | } else { 110 | console.log("CcTransferRouterProxy is already minter") 111 | } 112 | 113 | const isCCExchangeMinter = await lockersInstance.isMinter( 114 | ccExchangeRouterProxy.address 115 | ) 116 | 117 | if (!isCCExchangeMinter) { 118 | const addCCExchangeAsMinter = await lockersInstance.addMinter( 119 | ccExchangeRouterProxy.address 120 | ) 121 | 122 | await addCCExchangeAsMinter.wait(1) 123 | console.log("Added CcExchangeRouterProxy as minter: ", addCCExchangeAsMinter.hash) 124 | } else { 125 | console.log("CcExchangeRouterProxy is already minter") 126 | } 127 | 128 | const isCCBurnerBurner = await lockersInstance.isBurner( 129 | burnRouterProxy.address 130 | ) 131 | 132 | if (!isCCBurnerBurner) { 133 | const addCCBurnerAsBurner = await lockersInstance.addBurner( 134 | burnRouterProxy.address 135 | ) 136 | 137 | await addCCBurnerAsBurner.wait(1) 138 | console.log("Added BurnRouterProxy as burner: ", addCCBurnerAsBurner.hash) 139 | } else { 140 | console.log("BurnRouterProxy is already burner") 141 | } 142 | 143 | logger.color('blue').log("-------------------------------------------------") 144 | logger.color('blue').bold().log("Set PriceOracle in Lockers ...") 145 | 146 | const _priceOracleAddress = await lockersInstance.priceOracle() 147 | 148 | if (_priceOracleAddress.toLowerCase() != priceOracle.address.toLowerCase()) { 149 | const addPriceOracle = await lockersInstance.setPriceOracle( 150 | priceOracle.address 151 | ) 152 | await addPriceOracle.wait(1) 153 | console.log("Set PriceOracle in Lockers: ", addPriceOracle.hash) 154 | } else { 155 | console.log("PriceOracle is already set") 156 | } 157 | 158 | logger.color('blue').log("-------------------------------------------------") 159 | logger.color('blue').bold().log("Set BurnRouterProxy in Lockers ...") 160 | 161 | const burnRouterProxyAddress = await lockersInstance.ccBurnRouter() 162 | 163 | if (burnRouterProxyAddress != burnRouterProxy.address) { 164 | const addCCBurnRouter = await lockersInstance.setCCBurnRouter( 165 | burnRouterProxy.address 166 | ) 167 | 168 | await addCCBurnRouter.wait(1) 169 | console.log("Set BurnRouterProxy in Lockers: ", addCCBurnRouter.hash) 170 | } else { 171 | console.log("BurnRouterProxy is already set") 172 | } 173 | 174 | logger.color('blue').log("-------------------------------------------------") 175 | 176 | logger.color('blue').log("-------------------------------------------------") 177 | logger.color('blue').bold().log("Set LockersProxy as minter and burner in teleBTC ...") 178 | 179 | const teleBTCLogicFactory = await ethers.getContractFactory("TeleBTCLogic"); 180 | const teleBTCInstance = await teleBTCLogicFactory.attach( 181 | teleBTC.address 182 | ); 183 | 184 | const isLockersProxyMinter = await teleBTCInstance.minters( 185 | lockersProxy.address 186 | ) 187 | 188 | if (!isLockersProxyMinter) { 189 | const addLockerAsMinter = await teleBTCInstance.addMinter( 190 | lockersProxy.address 191 | ) 192 | await addLockerAsMinter.wait(1) 193 | console.log("Added LockersProxy as minter: ", addLockerAsMinter.hash) 194 | } else { 195 | console.log("LockersProxy is already minter") 196 | } 197 | 198 | const isLockersProxyBurner = await teleBTCInstance.burners( 199 | lockersProxy.address 200 | ) 201 | 202 | if (!isLockersProxyBurner) { 203 | const addLockerAsBurner = await teleBTCInstance.addBurner( 204 | lockersProxy.address 205 | ) 206 | 207 | await addLockerAsBurner.wait(1) 208 | console.log("Added LockersProxy as burner: ", addLockerAsBurner.hash) 209 | } else { 210 | console.log("LockersProxy is already burner") 211 | } 212 | 213 | logger.color('blue').log("-------------------------------------------------") 214 | logger.color('blue').bold().log("Set ExchangeRouter in ExchangeConnector ...") 215 | 216 | const uniswapV2Router02 = config.get("uniswap_v2_router_02") 217 | const uniswapV2Connector = await deployments.get("UniswapV2Connector") 218 | 219 | const uniswapV2ConnectorFactory = await ethers.getContractFactory("UniswapV2Connector"); 220 | const uniswapV2ConnectorInstance = await uniswapV2ConnectorFactory.attach( 221 | uniswapV2Connector.address 222 | ); 223 | 224 | const _exchangeRouter = await uniswapV2ConnectorInstance.exchangeRouter(); 225 | if (uniswapV2Router02 != _exchangeRouter) { 226 | const setExchangeRouterTx = await uniswapV2ConnectorInstance.setExchangeRouter( 227 | uniswapV2Router02 228 | ) 229 | await setExchangeRouterTx.wait(1) 230 | console.log("Set ExchangeRouter in ExchangeConnector", setExchangeRouterTx.hash) 231 | } else { 232 | console.log("ExchangeRouter is already set") 233 | } 234 | 235 | logger.color('blue').log("-------------------------------------------------") 236 | logger.color('blue').bold().log("Set ExchangeRouter in PriceOracle ...") 237 | 238 | const priceOracleFactory = await ethers.getContractFactory("PriceOracle"); 239 | const priceOracleInstance = await priceOracleFactory.attach( 240 | priceOracle.address 241 | ); 242 | 243 | const exchangeConnectorAddress = await priceOracleInstance.exchangeConnector( 244 | uniswapV2Router02 245 | ) 246 | 247 | if (exchangeConnectorAddress == ZERO_ADD) { 248 | const addExchangeTx = await priceOracleInstance.addExchangeConnector( 249 | uniswapV2Router02, 250 | uniswapV2Connector.address 251 | ); 252 | await addExchangeTx.wait(1) 253 | console.log("Set ExchangeRouter in PriceOracle: ", addExchangeTx.hash) 254 | } else { 255 | console.log("ExchangeRouter is already set") 256 | } 257 | 258 | logger.color('blue').log("-------------------------------------------------") 259 | logger.color('blue').bold().log("Set PriceProxies to PriceOracle ...") 260 | 261 | const wrappedNativeToken = config.get("wrapped_native_token") 262 | const nativeTokenUSDOracle = config.get("chain_link_oracles.native_token_usd"); 263 | 264 | let tx; 265 | const checkNativeTokenUSDTx = await priceOracleInstance.ChainlinkPriceProxy( 266 | wrappedNativeToken 267 | ) 268 | 269 | if (checkNativeTokenUSDTx != nativeTokenUSDOracle) { 270 | tx = await priceOracleInstance.setPriceProxy( 271 | wrappedNativeToken, 272 | nativeTokenUSDOracle 273 | ) 274 | tx.wait(1) 275 | console.log("Set NativeToken/USD in PriceOracle: ", tx.hash) 276 | } else { 277 | console.log("NativeToken/USD is already set") 278 | } 279 | 280 | const ONE_ADD = "0x0000000000000000000000000000000000000001" 281 | const checkNativeTokenUSDTx2 = await priceOracleInstance.ChainlinkPriceProxy( 282 | ONE_ADD 283 | ) 284 | 285 | if (checkNativeTokenUSDTx2 != nativeTokenUSDOracle) { 286 | tx = await priceOracleInstance.setPriceProxy( 287 | ONE_ADD, 288 | nativeTokenUSDOracle 289 | ) 290 | tx.wait(1) 291 | console.log("Set NativeToken/USD (ONE_ADD) in PriceOracle: ", tx.hash) 292 | } else { 293 | console.log("NativeToken/USD (ONE_ADD) is already set") 294 | } 295 | 296 | // const tBTC = await deployments.get("TeleBTC") 297 | const tBTC = await deployments.get("TeleBTCProxy") 298 | const btcUSDOracle = config.get("chain_link_oracles.btc_usd"); 299 | 300 | const checkBitcoinUSDTx = await priceOracleInstance.ChainlinkPriceProxy( 301 | tBTC.address 302 | ) 303 | 304 | if (checkBitcoinUSDTx != btcUSDOracle) { 305 | tx = await priceOracleInstance.setPriceProxy( 306 | tBTC.address, 307 | btcUSDOracle 308 | ) 309 | tx.wait(1) 310 | console.log("Set BTC/USD in PriceOracle: ", tx.hash) 311 | } else { 312 | console.log("BTC/USD is already set") 313 | } 314 | 315 | const usdt = config.get("usdt_token") 316 | const usdtUSDOracle = config.get("chain_link_oracles.usdt_usd"); 317 | 318 | const checkUsdtUSDTx = await priceOracleInstance.ChainlinkPriceProxy( 319 | usdt 320 | ) 321 | 322 | if (checkUsdtUSDTx != usdtUSDOracle) { 323 | tx = await priceOracleInstance.setPriceProxy( 324 | usdt, 325 | usdtUSDOracle 326 | ) 327 | tx.wait(1) 328 | console.log("Set USDT/USD in PriceOracle: ", tx.hash) 329 | } else { 330 | console.log("USDT/USD is already set") 331 | } 332 | 333 | const usdc = config.get("usdc_token") 334 | const usdcUSDOracle = config.get("chain_link_oracles.usdc_usd"); 335 | 336 | const checkUsdcUSDTx = await priceOracleInstance.ChainlinkPriceProxy( 337 | usdc 338 | ) 339 | 340 | if (checkUsdcUSDTx != usdcUSDOracle) { 341 | tx = await priceOracleInstance.setPriceProxy( 342 | usdc, 343 | usdcUSDOracle 344 | ) 345 | tx.wait(1) 346 | console.log("Set USDC/USD in PriceOracle: ", tx.hash) 347 | } else { 348 | console.log("USDC/USD is already set") 349 | } 350 | 351 | }; 352 | 353 | export default func; 354 | -------------------------------------------------------------------------------- /contracts/libraries/LockersLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.8.4; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import "../oracle/interfaces/IPriceOracle.sol"; 6 | import "../erc20/interfaces/ITeleBTC.sol"; 7 | import "../types/DataTypes.sol"; 8 | import "@teleportdao/btc-evm-bridge/contracts/types/ScriptTypesEnum.sol"; 9 | 10 | library LockersLib { 11 | 12 | function requestToBecomeLockerValidation( 13 | mapping(address => DataTypes.locker) storage lockersMapping, 14 | DataTypes.lockersLibParam memory libParams, 15 | address theLockerTargetAddress, 16 | uint _lockedTDTAmount, 17 | uint _lockedNativeTokenAmount 18 | ) external { 19 | 20 | require( 21 | !lockersMapping[msg.sender].isCandidate, 22 | "Lockers: is candidate" 23 | ); 24 | 25 | require( 26 | !lockersMapping[msg.sender].isLocker, 27 | "Lockers: is locker" 28 | ); 29 | 30 | require( 31 | _lockedTDTAmount >= libParams.minRequiredTDTLockedAmount, 32 | "Lockers: low TDT" 33 | ); 34 | 35 | require( 36 | _lockedNativeTokenAmount >= libParams.minRequiredTNTLockedAmount && msg.value == _lockedNativeTokenAmount, 37 | "Lockers: low TNT" 38 | ); 39 | 40 | require( 41 | theLockerTargetAddress == address(0), 42 | "Lockers: used locking script" 43 | ); 44 | 45 | } 46 | 47 | function requestToBecomeLocker( 48 | mapping(address => DataTypes.locker) storage lockersMapping, 49 | bytes calldata _candidateLockingScript, 50 | uint _lockedTDTAmount, 51 | uint _lockedNativeTokenAmount, 52 | ScriptTypes _lockerRescueType, 53 | bytes calldata _lockerRescueScript 54 | ) external { 55 | 56 | DataTypes.locker memory locker_; 57 | locker_.lockerLockingScript = _candidateLockingScript; 58 | locker_.TDTLockedAmount = _lockedTDTAmount; 59 | locker_.nativeTokenLockedAmount = _lockedNativeTokenAmount; 60 | locker_.isCandidate = true; 61 | locker_.lockerRescueType = _lockerRescueType; 62 | locker_.lockerRescueScript = _lockerRescueScript; 63 | 64 | lockersMapping[msg.sender] = locker_; 65 | 66 | } 67 | 68 | function buySlashedCollateralOfLocker( 69 | DataTypes.locker storage theLocker, 70 | uint _collateralAmount 71 | ) external returns (uint neededTeleBTC) { 72 | 73 | require( 74 | theLocker.isLocker, 75 | "Lockers: input address is not a valid locker" 76 | ); 77 | 78 | require( 79 | _collateralAmount <= theLocker.reservedNativeTokenForSlash, 80 | "Lockers: not enough slashed collateral to buy" 81 | ); 82 | 83 | neededTeleBTC = theLocker.slashingTeleBTCAmount * _collateralAmount / theLocker.reservedNativeTokenForSlash; 84 | 85 | if (neededTeleBTC < theLocker.slashingTeleBTCAmount) { 86 | // to avoid precision loss (so buyer cannot profit of it) 87 | neededTeleBTC = neededTeleBTC + 1; 88 | } 89 | 90 | // Updates locker's slashing info 91 | theLocker.slashingTeleBTCAmount = 92 | theLocker.slashingTeleBTCAmount - neededTeleBTC; 93 | 94 | theLocker.reservedNativeTokenForSlash = 95 | theLocker.reservedNativeTokenForSlash - _collateralAmount; 96 | 97 | } 98 | 99 | function liquidateLocker( 100 | DataTypes.locker storage theLocker, 101 | DataTypes.lockersLibConstants memory libConstants, 102 | DataTypes.lockersLibParam memory libParams, 103 | uint _collateralAmount 104 | ) external view returns (uint neededTeleBTC) { 105 | 106 | require( 107 | theLocker.isLocker, 108 | "Lockers: input address is not a valid locker" 109 | ); 110 | 111 | // DataTypes.locker memory theLiquidatingLocker = lockersMapping[_lockerTargetAddress]; 112 | uint priceOfCollateral = priceOfOneUnitOfCollateralInBTC( 113 | libConstants, 114 | libParams 115 | ); 116 | 117 | // Checks that the collateral has become unhealthy 118 | require( 119 | calculateHealthFactor( 120 | theLocker, 121 | libConstants, 122 | libParams, 123 | priceOfCollateral 124 | ) < libConstants.HealthFactor, 125 | "Lockers: is healthy" 126 | ); 127 | 128 | uint _maxBuyableCollateral = maximumBuyableCollateral( 129 | theLocker, 130 | libConstants, 131 | libParams, 132 | priceOfCollateral 133 | ); 134 | 135 | if (_maxBuyableCollateral > theLocker.nativeTokenLockedAmount) { 136 | _maxBuyableCollateral = theLocker.nativeTokenLockedAmount; 137 | } 138 | 139 | require( 140 | _collateralAmount <= _maxBuyableCollateral, 141 | "Lockers: not enough collateral to buy" 142 | ); 143 | 144 | // Needed amount of TeleBTC to buy collateralAmount 145 | neededTeleBTC = neededTeleBTCToBuyCollateral( 146 | libConstants, 147 | libParams, 148 | _collateralAmount, 149 | priceOfCollateral 150 | ); 151 | 152 | neededTeleBTC = neededTeleBTC + 1; // to prevent precision loss 153 | 154 | } 155 | 156 | function slashThiefLocker( 157 | DataTypes.locker storage theLocker, 158 | DataTypes.lockersLibConstants memory libConstants, 159 | DataTypes.lockersLibParam memory libParams, 160 | uint _rewardAmount, 161 | uint _amount 162 | ) external returns (uint rewardInNativeToken, uint neededNativeTokenForSlash) { 163 | 164 | require( 165 | theLocker.isLocker, 166 | "Lockers: input address is not a valid locker" 167 | ); 168 | 169 | uint equivalentNativeToken = IPriceOracle(libParams.priceOracle).equivalentOutputAmount( 170 | _amount, // Total amount of TeleBTC that is slashed 171 | ITeleBTC(libParams.teleBTC).decimals(), // Decimal of teleBTC 172 | libConstants.NativeTokenDecimal, // Decimal of TNT 173 | libParams.teleBTC, // Input token 174 | libConstants.NativeToken // Output token 175 | ); 176 | 177 | rewardInNativeToken = equivalentNativeToken*_rewardAmount/_amount; 178 | neededNativeTokenForSlash = equivalentNativeToken*libParams.liquidationRatio/libConstants.OneHundredPercent; 179 | 180 | if ((rewardInNativeToken + neededNativeTokenForSlash) > theLocker.nativeTokenLockedAmount) { 181 | // Divides total locker's collateral proportional to reward amount and slash amount 182 | rewardInNativeToken = rewardInNativeToken*theLocker.nativeTokenLockedAmount/ 183 | (rewardInNativeToken + neededNativeTokenForSlash); 184 | neededNativeTokenForSlash = theLocker.nativeTokenLockedAmount - rewardInNativeToken; 185 | } 186 | 187 | // Updates locker's bond (in TNT) 188 | theLocker.nativeTokenLockedAmount 189 | = theLocker.nativeTokenLockedAmount - (rewardInNativeToken + neededNativeTokenForSlash); 190 | 191 | if (_amount > theLocker.netMinted) { 192 | _amount = theLocker.netMinted; 193 | } 194 | 195 | theLocker.netMinted 196 | = theLocker.netMinted - _amount; 197 | 198 | theLocker.slashingTeleBTCAmount 199 | = theLocker.slashingTeleBTCAmount + _amount; 200 | 201 | theLocker.reservedNativeTokenForSlash 202 | = theLocker.reservedNativeTokenForSlash + neededNativeTokenForSlash; 203 | } 204 | 205 | function slashIdleLocker( 206 | DataTypes.locker storage theLocker, 207 | DataTypes.lockersLibConstants memory libConstants, 208 | DataTypes.lockersLibParam memory libParams, 209 | uint _rewardAmount, 210 | uint _amount 211 | ) external returns (uint equivalentNativeToken) { 212 | 213 | require( 214 | theLocker.isLocker, 215 | "Lockers: input address is not a valid locker" 216 | ); 217 | 218 | equivalentNativeToken = IPriceOracle(libParams.priceOracle).equivalentOutputAmount( 219 | _rewardAmount + _amount, // Total amount of TeleBTC that is slashed 220 | ITeleBTC(libParams.teleBTC).decimals(), // Decimal of teleBTC 221 | libConstants.NativeTokenDecimal, // Decimal of TNT 222 | libParams.teleBTC, // Input token 223 | libConstants.NativeToken // Output token 224 | ); 225 | 226 | if (equivalentNativeToken > theLocker.nativeTokenLockedAmount) { 227 | equivalentNativeToken = theLocker.nativeTokenLockedAmount; 228 | } 229 | 230 | // Updates locker's bond (in TNT) 231 | theLocker.nativeTokenLockedAmount 232 | = theLocker.nativeTokenLockedAmount - equivalentNativeToken; 233 | } 234 | 235 | function maximumBuyableCollateral( 236 | DataTypes.locker storage theLocker, 237 | DataTypes.lockersLibConstants memory libConstants, 238 | DataTypes.lockersLibParam memory libParams, 239 | uint _priceOfOneUnitOfCollateral 240 | ) public view returns (uint) { 241 | 242 | // maxBuyable <= (upperHealthFactor*netMinted*liquidationRatio/10000 - nativeTokenLockedAmount*nativeTokenPrice)/(upperHealthFactor*liquidationRatio*discountedPrice - nativeTokenPrice) 243 | // => maxBuyable <= (upperHealthFactor*netMinted*liquidationRatio * 10^18 - nativeTokenLockedAmount*nativeTokenPrice * 10^8)/(upperHealthFactor*liquidationRatio*discountedPrice - nativeTokenPrice * 10^8) 244 | 245 | uint teleBTCDecimal = ERC20(libParams.teleBTC).decimals(); 246 | 247 | uint antecedent = (libConstants.UpperHealthFactor * theLocker.netMinted * libParams.liquidationRatio * (10 ** libConstants.NativeTokenDecimal)) - 248 | (theLocker.nativeTokenLockedAmount * _priceOfOneUnitOfCollateral * (10 ** teleBTCDecimal)); 249 | 250 | uint consequent = ((libConstants.UpperHealthFactor * libParams.liquidationRatio * _priceOfOneUnitOfCollateral * libParams.priceWithDiscountRatio)/libConstants.OneHundredPercent) - 251 | (_priceOfOneUnitOfCollateral * (10 ** teleBTCDecimal)); 252 | 253 | return antecedent/consequent; 254 | } 255 | 256 | function calculateHealthFactor( 257 | DataTypes.locker storage theLocker, 258 | DataTypes.lockersLibConstants memory libConstants, 259 | DataTypes.lockersLibParam memory libParams, 260 | uint _priceOfOneUnitOfCollateral 261 | ) public view returns (uint) { 262 | return (_priceOfOneUnitOfCollateral * theLocker.nativeTokenLockedAmount * 263 | (10 ** (1 + ERC20(libParams.teleBTC).decimals())))/ 264 | (theLocker.netMinted * libParams.liquidationRatio * (10 ** (1 + libConstants.NativeTokenDecimal))); 265 | } 266 | 267 | function neededTeleBTCToBuyCollateral( 268 | DataTypes.lockersLibConstants memory libConstants, 269 | DataTypes.lockersLibParam memory libParams, 270 | uint _collateralAmount, 271 | uint _priceOfCollateral 272 | ) public pure returns (uint) { 273 | return (_collateralAmount * _priceOfCollateral * libParams.priceWithDiscountRatio)/ 274 | (libConstants.OneHundredPercent*(10 ** libConstants.NativeTokenDecimal)); 275 | } 276 | 277 | function addToCollateral( 278 | DataTypes.locker storage theLocker, 279 | uint _addingNativeTokenAmount 280 | ) external { 281 | 282 | require( 283 | theLocker.isLocker, 284 | "Lockers: no locker" 285 | ); 286 | 287 | theLocker.nativeTokenLockedAmount = 288 | theLocker.nativeTokenLockedAmount + _addingNativeTokenAmount; 289 | } 290 | 291 | function removeFromCollateral( 292 | DataTypes.locker storage theLocker, 293 | DataTypes.lockersLibConstants memory libConstants, 294 | DataTypes.lockersLibParam memory libParams, 295 | uint _priceOfOneUnitOfCollateral, 296 | uint _removingNativeTokenAmount 297 | ) internal { 298 | 299 | require( 300 | theLocker.isLocker, 301 | "Lockers: account is not a locker" 302 | ); 303 | 304 | // Capacity of locker = (locker's collateral value in TeleBTC) * (collateral ratio) - (minted TeleBTC) 305 | uint lockerCapacity = (theLocker.nativeTokenLockedAmount * _priceOfOneUnitOfCollateral * 306 | libConstants.OneHundredPercent)/ 307 | (libParams.collateralRatio * (10 ** libConstants.NativeTokenDecimal)) - theLocker.netMinted; 308 | 309 | uint maxRemovableCollateral = (lockerCapacity * (10 ** libConstants.NativeTokenDecimal))/_priceOfOneUnitOfCollateral; 310 | 311 | require( 312 | _removingNativeTokenAmount <= maxRemovableCollateral, 313 | "Lockers: more than max removable collateral" 314 | ); 315 | 316 | require( 317 | theLocker.nativeTokenLockedAmount - _removingNativeTokenAmount >= libParams.minRequiredTNTLockedAmount, 318 | "Lockers: less than min collateral" 319 | ); 320 | 321 | theLocker.nativeTokenLockedAmount = 322 | theLocker.nativeTokenLockedAmount - _removingNativeTokenAmount; 323 | } 324 | 325 | function priceOfOneUnitOfCollateralInBTC( 326 | DataTypes.lockersLibConstants memory libConstants, 327 | DataTypes.lockersLibParam memory libParams 328 | ) public view returns (uint) { 329 | 330 | return IPriceOracle(libParams.priceOracle).equivalentOutputAmount( 331 | (10**libConstants.NativeTokenDecimal), // 1 Ether is 10^18 wei 332 | libConstants.NativeTokenDecimal, 333 | ITeleBTC(libParams.teleBTC).decimals(), 334 | libConstants.NativeToken, 335 | libParams.teleBTC 336 | ); 337 | 338 | } 339 | 340 | function lockerCollateralInTeleBTC( 341 | DataTypes.locker storage theLocker, 342 | DataTypes.lockersLibConstants memory libConstants, 343 | DataTypes.lockersLibParam memory libParams 344 | ) public view returns (uint) { 345 | 346 | return IPriceOracle(libParams.priceOracle).equivalentOutputAmount( 347 | theLocker.nativeTokenLockedAmount, 348 | libConstants.NativeTokenDecimal, 349 | ITeleBTC(libParams.teleBTC).decimals(), 350 | libConstants.NativeToken, 351 | libParams.teleBTC 352 | ); 353 | } 354 | 355 | } -------------------------------------------------------------------------------- /test/test_fixtures/ccExchangeRequests.json: -------------------------------------------------------------------------------- 1 | { 2 | "normalCCExchange_fixedInput": { 3 | "txId": "0x47b4ca636567ba248e2b1f46fc0ef7023269ddb8b7cb0cf984df0fee5d3d6d5f", 4 | "version": "0x02000000", 5 | "vin": "0x014733e8212b204c372ad637f569ce4eab5c9206b791e69a55a5facb02f8cc0cd50100000000feffffff", 6 | "vout": "0x03102700000000000017a9144062c8aeed4f81c2d73ff854a2957021191e20b6870000000000000000526a4c4f01000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX000000000000000000000000000000000000000000000000000000119999999901533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 7 | "opReturn": "01000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX000000000000000000000000000000000000000000000000000000119999999901", 8 | "locktime": "0x00000000", 9 | "blockNumber": 283, 10 | "intermediateNodes": "0x1bc04ab7091f1608b47ed4ec422ede9496c377f4dc139a3afcf7bced9e9a9e50", 11 | "index": 1, 12 | "bitcoinAmount": 10000, 13 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 14 | "chainId": 1, 15 | "appId": 1, 16 | "teleporterFee": 100, 17 | "isExchange": 1, 18 | "speed": 0, 19 | "exchangeTokenAddress": "0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 20 | "exchangeAmount": 17, 21 | "deadline": 2576980377, 22 | "isFixedToken": 1, 23 | "desiredRecipient": "0x4062c8aeed4f81c2d73ff854a2957021191e20b6" 24 | }, 25 | "normalCCExchange_fixedOutput": { 26 | "txId": "0x1dca9ad373df56e780de7a24c6d76b8e820179b529faef0713996dfee64fd295", 27 | "version": "0x02000000", 28 | "vin": "0x014733e8212b204c372ad637f569ce4eab5c9206b791e69a55a5facb02f8cc0cd50100000000feffffff", 29 | "vout": "0x03102700000000000017a9144062c8aeed4f81c2d73ff854a2957021191e20b6870000000000000000526a4c4f01000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX000000000000000000000000000000000000000000000000000000649999999900533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 30 | "opReturn": "01000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX000000000000000000000000000000000000000000000000000000649999999900", 31 | "locktime": "0x00000000", 32 | "blockNumber": 283, 33 | "intermediateNodes": "0x1bc04ab7091f1608b47ed4ec422ede9496c377f4dc139a3afcf7bced9e9a9e50", 34 | "index": 1, 35 | "bitcoinAmount": 10000, 36 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 37 | "chainId": 1, 38 | "appId": 1, 39 | "teleporterFee": 100, 40 | "isExchange": 1, 41 | "speed": 0, 42 | "exchangeTokenAddress": "0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 43 | "exchangeAmount": 100, 44 | "deadline": 2576980377, 45 | "isFixedToken": 0, 46 | "desiredRecipient": "0x4062c8aeed4f81c2d73ff854a2957021191e20b6" 47 | }, 48 | "normalCCExchange_wrongLocker": { 49 | "txId": "0x1dca9ad373df56e780de7a24c6d76b8e820179b529faef0713996dfee64fd295", 50 | "version": "0x02000000", 51 | "vin": "0x014733e8212b204c372ad637f569ce4eab5c9206b791e69a55a5facb02f8cc0cd50100000000feffffff", 52 | "vout": "0x03102700000000000017a9141062c8aeed4f81c2d73ff854a2957021191e20b6870000000000000000526a4c4f01000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX000000000000000000000000000000000000000000000000000000119999999901533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 53 | "opReturn": "01000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX000000000000000000000000000000000000000000000000000000119999999901", 54 | "locktime": "0x00000000", 55 | "blockNumber": 283, 56 | "intermediateNodes": "0x1bc04ab7091f1608b47ed4ec422ede9496c377f4dc139a3afcf7bced9e9a9e50", 57 | "index": 1, 58 | "bitcoinAmount": 10000, 59 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 60 | "chainId": 1, 61 | "appId": 1, 62 | "teleporterFee": 100, 63 | "isExchange": 1, 64 | "speed": 0, 65 | "exchangeTokenAddress": "0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 66 | "exchangeAmount": 17, 67 | "deadline": 2576980377, 68 | "isFixedToken": 1, 69 | "desiredRecipient": "0x1062c8aeed4f81c2d73ff854a2957021191e20b6" 70 | }, 71 | "normalCCExchange_invalidChainId": { 72 | "txId": "0x1dca9ad373df56e780de7a24c6d76b8e820179b529faef0713996dfee64fd295", 73 | "version": "0x02000000", 74 | "vin": "0x014733e8212b204c372ad637f569ce4eab5c9206b791e69a55a5facb02f8cc0cd50100000000feffffff", 75 | "vout": "0x03102700000000000017a9144062c8aeed4f81c2d73ff854a2957021191e20b6870000000000000000526a4c4f02000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX000000000000000000000000000000000000000000000000000000119999999901533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 76 | "opReturn": "02000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX000000000000000000000000000000000000000000000000000000119999999901", 77 | "locktime": "0x00000000", 78 | "blockNumber": 283, 79 | "intermediateNodes": "0x1bc04ab7091f1608b47ed4ec422ede9496c377f4dc139a3afcf7bced9e9a9e50", 80 | "index": 1, 81 | "bitcoinAmount": 10000, 82 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 83 | "chainId": 2, 84 | "appId": 1, 85 | "teleporterFee": 100, 86 | "isExchange": 1, 87 | "speed": 0, 88 | "exchangeTokenAddress": "0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 89 | "exchangeAmount": 17, 90 | "deadline": 2576980377, 91 | "isFixedToken": 1, 92 | "desiredRecipient": "0x4062c8aeed4f81c2d73ff854a2957021191e20b6" 93 | }, 94 | "normalCCExchange_invalidAppId": { 95 | "txId": "0x1dca9ad373df56e780de7a24c6d76b8e820179b529faef0713996dfee64fd295", 96 | "version": "0x02000000", 97 | "vin": "0x014733e8212b204c372ad637f569ce4eab5c9206b791e69a55a5facb02f8cc0cd50100000000feffffff", 98 | "vout": "0x03102700000000000017a9144062c8aeed4f81c2d73ff854a2957021191e20b6870000000000000000526a4c4f01000282492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX000000000000000000000000000000000000000000000000000000119999999901533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 99 | "opReturn": "01000282492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX000000000000000000000000000000000000000000000000000000119999999901", 100 | "locktime": "0x00000000", 101 | "blockNumber": 283, 102 | "intermediateNodes": "0x1bc04ab7091f1608b47ed4ec422ede9496c377f4dc139a3afcf7bced9e9a9e50", 103 | "index": 1, 104 | "bitcoinAmount": 10000, 105 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 106 | "chainId": 1, 107 | "appId": 2, 108 | "teleporterFee": 100, 109 | "isExchange": 1, 110 | "speed": 0, 111 | "exchangeTokenAddress": "0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 112 | "exchangeAmount": 17, 113 | "deadline": 2576980377, 114 | "isFixedToken": 1, 115 | "desiredRecipient": "0x4062c8aeed4f81c2d73ff854a2957021191e20b6" 116 | }, 117 | "normalCCExchange_invalidFee": { 118 | "txId": "0x1dca9ad373df56e780de7a24c6d76b8e820179b529faef0713996dfee64fd295", 119 | "version": "0x02000000", 120 | "vin": "0x014733e8212b204c372ad637f569ce4eab5c9206b791e69a55a5facb02f8cc0cd50100000000feffffff", 121 | "vout": "0x03102700000000000017a9144062c8aeed4f81c2d73ff854a2957021191e20b6870000000000000000526a4c4f01000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447dffff00XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX000000000000000000000000000000000000000000000000000000119999999901533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 122 | "opReturn": "01000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447dffff00XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX000000000000000000000000000000000000000000000000000000119999999901", 123 | "locktime": "0x00000000", 124 | "blockNumber": 283, 125 | "intermediateNodes": "0x1bc04ab7091f1608b47ed4ec422ede9496c377f4dc139a3afcf7bced9e9a9e50", 126 | "index": 1, 127 | "bitcoinAmount": 10000, 128 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 129 | "chainId": 1, 130 | "appId": 1, 131 | "teleporterFee": 65535, 132 | "isExchange": 1, 133 | "speed": 0, 134 | "exchangeTokenAddress": "0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 135 | "exchangeAmount": 17, 136 | "deadline": 2576980377, 137 | "isFixedToken": 1, 138 | "desiredRecipient": "0x4062c8aeed4f81c2d73ff854a2957021191e20b6" 139 | }, 140 | "normalCCExchange_wrongSpeed": { 141 | "txId": "0x1dca9ad373df56e780de7a24c6d76b8e820179b529faef0713996dfee64fd295", 142 | "version": "0x02000000", 143 | "vin": "0x014733e8212b204c372ad637f569ce4eab5c9206b791e69a55a5facb02f8cc0cd50100000000feffffff", 144 | "vout": "0x03102700000000000017a9144062c8aeed4f81c2d73ff854a2957021191e20b6870000000000000000526a4c4f01000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006402XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX000000000000000000000000000000000000000000000000000000119999999901533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 145 | "opReturn": "01000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006402XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX000000000000000000000000000000000000000000000000000000119999999901", 146 | "locktime": "0x00000000", 147 | "blockNumber": 283, 148 | "intermediateNodes": "0x1bc04ab7091f1608b47ed4ec422ede9496c377f4dc139a3afcf7bced9e9a9e50", 149 | "index": 1, 150 | "bitcoinAmount": 10000, 151 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 152 | "chainId": 1, 153 | "appId": 1, 154 | "teleporterFee": 100, 155 | "isExchange": 1, 156 | "speed": 2, 157 | "exchangeTokenAddress": "0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 158 | "exchangeAmount": 17, 159 | "deadline": 2576980377, 160 | "isFixedToken": 1, 161 | "desiredRecipient": "0x4062c8aeed4f81c2d73ff854a2957021191e20b6" 162 | }, 163 | "normalCCExchange_expired": { 164 | "txId": "0x1dca9ad373df56e780de7a24c6d76b8e820179b529faef0713996dfee64fd295", 165 | "version": "0x02000000", 166 | "vin": "0x014733e8212b204c372ad637f569ce4eab5c9206b791e69a55a5facb02f8cc0cd50100000000feffffff", 167 | "vout": "0x03102700000000000017a9144062c8aeed4f81c2d73ff854a2957021191e20b6870000000000000000526a4c4f01000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447d0064008A791620dd6260079BF849Dc5567aDC3F2FdC318000000000000000000000000000000000000000000000000000000110000000000533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 168 | "opReturn": "01000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447d0064008A791620dd6260079BF849Dc5567aDC3F2FdC318000000000000000000000000000000000000000000000000000000110000000000", 169 | "locktime": "0x00000000", 170 | "blockNumber": 283, 171 | "intermediateNodes": "0x1bc04ab7091f1608b47ed4ec422ede9496c377f4dc139a3afcf7bced9e9a9e50", 172 | "index": 1, 173 | "bitcoinAmount": 10000, 174 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 175 | "teleporterFee": 100, 176 | "isExchange": 1, 177 | "speed": 0, 178 | "exchangeTokenAddress": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318", 179 | "exchangeAmount": 17, 180 | "deadline": 0, 181 | "isFixedToken": 0, 182 | "desiredRecipient": "0x4062c8aeed4f81c2d73ff854a2957021191e20b6" 183 | }, 184 | "normalCCExchange_highSlippage": { 185 | "txId": "0x1dca9ad373df56e780de7a24c6d76b8e820179b529faef0713996dfee64fd295", 186 | "version": "0x02000000", 187 | "vin": "0x014733e8212b204c372ad637f569ce4eab5c9206b791e69a55a5facb02f8cc0cd50100000000feffffff", 188 | "vout": "0x03102700000000000017a9144062c8aeed4f81c2d73ff854a2957021191e20b6870000000000000000526a4c4f01000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0000000000000000000000000000000000000000000000ffffffffff9999999901533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 189 | "opReturn": "01000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0000000000000000000000000000000000000000000000ffffffffff9999999901", 190 | "locktime": "0x00000000", 191 | "blockNumber": 283, 192 | "intermediateNodes": "0x1bc04ab7091f1608b47ed4ec422ede9496c377f4dc139a3afcf7bced9e9a9e50", 193 | "index": 1, 194 | "bitcoinAmount": 10000, 195 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 196 | "chainId": 1, 197 | "appId": 1, 198 | "teleporterFee": 100, 199 | "isExchange": 1, 200 | "speed": 0, 201 | "exchangeTokenAddress": "0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 202 | "exchangeAmount": 1099511627775, 203 | "deadline": 2576980377, 204 | "isFixedToken": 1, 205 | "desiredRecipient": "0x4062c8aeed4f81c2d73ff854a2957021191e20b6" 206 | }, 207 | "normalCCExchange_lowInput": { 208 | "txId": "0x1dca9ad373df56e780de7a24c6d76b8e820179b529faef0713996dfee64fd295", 209 | "version": "0x02000000", 210 | "vin": "0x014733e8212b204c372ad637f569ce4eab5c9206b791e69a55a5facb02f8cc0cd50100000000feffffff", 211 | "vout": "0x03102700000000000017a9144062c8aeed4f81c2d73ff854a2957021191e20b6870000000000000000526a4c4f01000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0000000000000000000000000000000000000000000000ffffffffff9999999900533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 212 | "opReturn": "01000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006400XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0000000000000000000000000000000000000000000000ffffffffff9999999900", 213 | "locktime": "0x00000000", 214 | "blockNumber": 283, 215 | "intermediateNodes": "0x1bc04ab7091f1608b47ed4ec422ede9496c377f4dc139a3afcf7bced9e9a9e50", 216 | "index": 1, 217 | "bitcoinAmount": 10000, 218 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 219 | "chainId": 1, 220 | "appId": 1, 221 | "teleporterFee": 100, 222 | "isExchange": 1, 223 | "speed": 0, 224 | "exchangeTokenAddress": "0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 225 | "exchangeAmount": 1099511627775, 226 | "deadline": 2576980377, 227 | "isFixedToken": 0, 228 | "desiredRecipient": "0x4062c8aeed4f81c2d73ff854a2957021191e20b6" 229 | }, 230 | "instantCCExchange": { 231 | "txId": "0x1dca9ad373df56e780de7a24c6d76b8e820179b529faef0713996dfee64fd295", 232 | "version": "0x02000000", 233 | "vin": "0x014733e8212b204c372ad637f569ce4eab5c9206b791e69a55a5facb02f8cc0cd50100000000feffffff", 234 | "vout": "0x03102700000000000017a9144062c8aeed4f81c2d73ff854a2957021191e20b6870000000000000000526a4c4f01000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006401XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX000000000000000000000000000000000000000000000000000000110022222200533b0000000000001600140c92227de5c4bbe76247335b078cf2de137285db", 235 | "opReturn": "01000182492cAFDD0BA0F68dec07Da75C28Fdb9d07447d006401XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX000000000000000000000000000000000000000000000000000000110022222200", 236 | "locktime": "0x00000000", 237 | "blockNumber": 283, 238 | "intermediateNodes": "0x1bc04ab7091f1608b47ed4ec422ede9496c377f4dc139a3afcf7bced9e9a9e50", 239 | "index": 1, 240 | "bitcoinAmount": 10000, 241 | "recipientAddress":"0x82492cAFDD0BA0F68dec07Da75C28Fdb9d07447d", 242 | "teleporterFee": 100, 243 | "isExchange": 1, 244 | "speed": 0, 245 | "exchangeTokenAddress": "0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 246 | "exchangeAmount": 17, 247 | "deadline": 2236962, 248 | "isFixedToken": 0, 249 | "desiredRecipient": "0x4062c8aeed4f81c2d73ff854a2957021191e20b6" 250 | } 251 | } --------------------------------------------------------------------------------