diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 130a421..392d513 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,5 +36,5 @@ jobs: - name: Run Forge tests run: | - forge test -vvv + forge test -vvvv id: test diff --git a/.gitignore b/.gitignore index 3269660..3736fb6 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,7 @@ out/ # Dotenv file .env + + +# Soldeer +/dependencies diff --git a/.vscode/settings.json b/.vscode/settings.json index ee57ec9..5e3c5d8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "solidity.compileUsingRemoteVersion": "v0.8.28+commit.7893614a" + "solidity.compileUsingRemoteVersion": "v0.8.28+commit.7893614a", + "editor.tabSize": 4 } \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 25b918f..d20d6c8 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,6 +1,10 @@ [profile.default] src = "src" out = "out" -libs = ["lib"] +libs = ["lib", "dependencies"] + +[dependencies] +forge-std = "1.9.5" +"@openzeppelin-contracts" = "5.2.0-rc.1" # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/remappings.txt b/remappings.txt new file mode 100644 index 0000000..0674b75 --- /dev/null +++ b/remappings.txt @@ -0,0 +1,2 @@ +@openzeppelin/contracts/=dependencies/@openzeppelin-contracts-5.2.0-rc.1/ +forge-std-1.9.5/=dependencies/forge-std-1.9.5/ diff --git a/soldeer.lock b/soldeer.lock new file mode 100644 index 0000000..78ef312 --- /dev/null +++ b/soldeer.lock @@ -0,0 +1,13 @@ +[[dependencies]] +name = "@openzeppelin-contracts" +version = "5.2.0-rc.1" +url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/5_2_0-rc_1_18-12-2024_19:26:33_contracts.zip" +checksum = "0430f56c556a4864cb2c0f28edbd497304a1e367d30dd07942778bef3a0f7a5f" +integrity = "17e24d71e2995a505f428ff1e4b514723175d8c6f7b84db6a96cfa31bc73fe23" + +[[dependencies]] +name = "forge-std" +version = "1.9.5" +url = "https://soldeer-revisions.s3.amazonaws.com/forge-std/1_9_5_21-12-2024_15:04:05_forge-std-1.9.zip" +checksum = "57ada736f383289db77fac4472d48f820e7c98172cf9b01681b0c37065ce043f" +integrity = "4753ffdfa0dde40878372b6a4d8e8fd1648b190b33996896c8b92f6f1680850f" diff --git a/src/BaseSlpx.sol b/src/BaseSlpx.sol index e9a54f2..34b7fc1 100644 --- a/src/BaseSlpx.sol +++ b/src/BaseSlpx.sol @@ -1,16 +1,28 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.25; +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; import "src/SlpxContracts/interfaces/IVETH.sol"; import "src/SlpxContracts/interfaces/snowbridge/IGateway.sol"; import "src/SlpxContracts/interfaces/snowbridge/MultiAddress.sol"; -contract EthereumSlpx { - address constant gateway = 0x74bAA141B18D5D1eeF1591abf37167FbeCE23B72; - address constant slpcore = 0x74bAA141B18D5D1eeF1591abf37167FbeCE23B72; - address constant veth = 0x4Bc3263Eb5bb2Ef7Ad9aB6FB68be80E43b43801F; - uint128 constant destinationFee = 1000000; - uint32 constant paraId = 2030; +contract BaseSlpx { + address public gateway; + address public slpcore; + address public veth; + uint128 public destinationFee; + uint32 public paraId; + + constructor(address _gateway) { + gateway = _gateway; + slpcore = _slpcore; + veth = _veth; + destinationFee = _destinationFee; + paraId = _paraId; + } + + /*////////////////////////////////////////////////////////////// + CORE LOGIC + //////////////////////////////////////////////////////////////*/ function mint() external payable { uint256 fee = IGateway(gateway).quoteSendTokenFee( veth, @@ -32,4 +44,32 @@ contract EthereumSlpx { uint128(vethAmount) ); } + + + /*////////////////////////////////////////////////////////////// + SETTERS + //////////////////////////////////////////////////////////////*/ + function setGateway(address _gateway) external { + gateway = _gateway; + } + + + function setSlpcore(address _slpcore) external { + slpcore = _slpcore; + } + + + function setVeth(address _veth) external { + veth = _veth; + } + + + function setDestinationFee(uint128 _destinationFee) external { + destinationFee = _destinationFee; + } + + + function setParaId(uint32 _paraId) external { + paraId = _paraId; + } } diff --git a/src/Gateway.sol b/src/Gateway.sol new file mode 100644 index 0000000..6a40aa5 --- /dev/null +++ b/src/Gateway.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + + +contract Gateway { + + uint256 sendTokenFee = 1000000; + + function quoteSendTokenFee(address token, uint32 paraId, uint128 destinationFee) external view returns (uint256) { + // Basic validation to use the parameters + require(token != address(0), "Invalid token address"); + require(paraId > 0, "Invalid paraId"); + require(destinationFee > 0, "Invalid destination fee"); + + // mock the fee + return sendTokenFee; + } + + + function setSendTokenFee(uint256 _fee) external { + sendTokenFee = _fee; + } + +} diff --git a/src/LSToken.sol b/src/LSToken.sol new file mode 100644 index 0000000..fc6ea4f --- /dev/null +++ b/src/LSToken.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +// Compatible with OpenZeppelin Contracts ^5.0.0 +pragma solidity ^0.8.22; + +import {ERC20} from "src/ERC20.sol"; + +contract LSToken is ERC20("LSToken", "LSToken", 18) { + + address public owner; + + constructor(address _owner) { + owner = _owner; + } + + modifier onlyOwner() { + require(owner == msg.sender); + _; + } + + function mint(address to, uint256 amount) public onlyOwner { + _mint(to, amount); + } + + function burn(address from, uint256 amount) public onlyOwner { + _burn(from, amount); + } + + function setOwner(address _owner) public onlyOwner { + owner = _owner; + } +} diff --git a/src/SlpxContracts/AstarReceiver.sol b/src/SlpxContracts/AstarReceiver.sol deleted file mode 100644 index e8ae44d..0000000 --- a/src/SlpxContracts/AstarReceiver.sol +++ /dev/null @@ -1,365 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; - -import "@openzeppelin/contracts/access/Ownable.sol"; -import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/utils/Create2.sol"; -import "./interfaces/IOFTReceiverV2.sol"; -import "./interfaces/IOFTV2.sol"; -import "./interfaces/IOFTWithFee.sol"; -import "./interfaces/XCM.sol"; -import "./interfaces/XCM_v2.sol"; -import "./interfaces/Types.sol"; -import "./utils/BuildCallData.sol"; -import "./utils/AddressToAccount.sol"; -import "./AstarSlpx.sol"; -import "./DerivativeContract.sol"; - -contract AstarReceiver is Ownable, IOFTReceiverV2 { - bytes1 private constant ASTAR_CHAIN_TYPE = 0x00; - bytes2 private constant ASTR_CURRENCY_ID = 0x0803; - bytes2 private constant VASTR_CURRENCY_ID = 0x0903; - uint256 private constant BIFROST_PARA_ID = 2030; - bool private constant IS_RELAY_CHAIN = false; - uint16 public constant destChainId = 257; - address public constant BNC = 0xfFffFffF00000000000000010000000000000007; - address public constant VASTR = 0xfffFffff00000000000000010000000000000010; - address public constant astarSlpx = - 0xc6bf0C5C78686f1D0E2E54b97D6de6e2cEFAe9fD; - address public constant polkadotXcm = - 0x0000000000000000000000000000000000005004; - address public constant astrNativeOFT = - 0xdf41220C7e322bFEF933D85D01821ad277f90172; - address public constant vAstrProxyOFT = - 0xba273b7Fa296614019c71Dcc54DCa6C922A93BcF; - address public astarZkSlpx; - uint256 public astrLayerZeroFee; - uint256 public vastrLayerZeroFee; - address public scriptTrigger; - mapping(address => address) public callerToDerivativeAddress; - mapping(address => bool) public isDerivativeAddress; - - event Mint( - address indexed caller, - address indexed derivativeAddress, - uint256 indexed amount - ); - event Redeem( - address indexed caller, - address indexed derivativeAddress, - uint256 indexed amount - ); - event SetDerivativeAddress( - address indexed caller, - address indexed derivativeAddress - ); - event Receive(address indexed sender, uint256 indexed amount); - event SetLayerZeroFee( - address indexed scriptTrigger, - uint256 indexed astrFee, - uint256 indexed vastrFee - ); - event SetScriptTrigger(address indexed scriptTrigger); - - constructor(address _astarZkSlpx) { - require(_astarZkSlpx != address(0), "Invalid astarZkSlpx"); - astarZkSlpx = _astarZkSlpx; - } - - function zkSlpxMint(address _from, address _to, uint256 _amount) internal { - require(_from != address(0), "Invalid from"); - require(_to != address(0), "Invalid to"); - require(_amount != 0, "Invalid amount"); - xcmTransferNativeAsset(_from, _amount); - - bytes memory targetChain = abi.encodePacked(ASTAR_CHAIN_TYPE, _to); - bytes memory callData = BuildCallData.buildMintCallBytes( - _from, - ASTR_CURRENCY_ID, - targetChain, - "AstarZkEvm" - ); - (uint64 transactWeight, uint256 feeAmount) = AstarSlpx(astarSlpx) - .operationToFeeInfo(AstarSlpx.Operation.Mint); - - // xcm transact - require( - XCM(polkadotXcm).remote_transact( - BIFROST_PARA_ID, - IS_RELAY_CHAIN, - BNC, - feeAmount, - callData, - transactWeight - ), - "Failed to send xcm" - ); - emit Mint(_from, _to, _amount); - } - - function zkSlpxRedeem( - address _from, - address _to, - uint256 _amount - ) internal { - require(_from != address(0), "Invalid from"); - require(_to != address(0), "Invalid to"); - require(_amount != 0, "Invalid amount"); - xcmTransferAsset(VASTR, _from, _amount); - - bytes memory targetChain = abi.encodePacked(ASTAR_CHAIN_TYPE, _to); - bytes memory callData = BuildCallData.buildRedeemCallBytes( - _from, - VASTR_CURRENCY_ID, - targetChain - ); - (uint64 transactWeight, uint256 feeAmount) = AstarSlpx(astarSlpx) - .operationToFeeInfo(AstarSlpx.Operation.Redeem); - - // xcm transact - require( - XCM(polkadotXcm).remote_transact( - BIFROST_PARA_ID, - IS_RELAY_CHAIN, - BNC, - feeAmount, - callData, - transactWeight - ), - "Failed to send xcm" - ); - emit Redeem(_from, _to, _amount); - } - - function onOFTReceived( - uint16 _srcChainId, - bytes calldata, - uint64, - bytes32 _from, - uint _amount, - bytes calldata _payload - ) external override { - require(_srcChainId == destChainId, "only receive msg from astar-zk"); - require( - _msgSender() == astrNativeOFT || _msgSender() == vAstrProxyOFT, - "only native oft can call" - ); - require( - address(uint160(uint(_from))) == astarZkSlpx, - "only receive msg from astarZkSlpx" - ); - (address caller, Types.Operation operation) = abi.decode( - _payload, - (address, Types.Operation) - ); - if (callerToDerivativeAddress[caller] == address(0)) { - setDerivativeAddress(caller); - } - - if (operation == Types.Operation.Mint) { - IOFTWithFee(astrNativeOFT).withdraw(_amount); - (bool success, ) = scriptTrigger.call{value: astrLayerZeroFee}(""); - require(success, "failed to charge"); - zkSlpxMint( - caller, - callerToDerivativeAddress[caller], - _amount - astrLayerZeroFee - ); - } else if (operation == Types.Operation.Redeem) { - bool success = IERC20(VASTR).transfer( - scriptTrigger, - vastrLayerZeroFee - ); - require(success, "failed to charge"); - zkSlpxRedeem( - caller, - callerToDerivativeAddress[caller], - _amount - vastrLayerZeroFee - ); - } - } - - function claimVAstr( - address addr, - bytes calldata _adapterParams - ) external payable { - require(_msgSender() == scriptTrigger, "must be scriptTrigger"); - address derivativeAddress = callerToDerivativeAddress[addr]; - require(derivativeAddress != address(0), "invalid address"); - uint256 amount = DerivativeContract(derivativeAddress) - .withdrawErc20Token(VASTR); - IERC20(VASTR).approve(vAstrProxyOFT, amount); - ICommonOFT.LzCallParams memory callParams = ICommonOFT.LzCallParams( - payable(_msgSender()), - address(0), - _adapterParams - ); - bytes32 toAddress = bytes32(uint256(uint160(addr))); - (uint256 estimateFee, ) = IOFTV2(vAstrProxyOFT).estimateSendFee( - destChainId, - toAddress, - amount, - false, - _adapterParams - ); - require(msg.value >= estimateFee, "too small fee"); - if (msg.value != estimateFee) { - uint256 refundAmount = msg.value - estimateFee; - (bool success, ) = _msgSender().call{value: refundAmount}(""); - require(success, "failed to refund"); - } - IOFTV2(vAstrProxyOFT).sendFrom{value: estimateFee}( - address(this), - destChainId, - toAddress, - amount, - callParams - ); - } - - function claimAstr( - address addr, - uint256 _amount, - uint256 _minAmount, - bytes calldata _adapterParams - ) external payable { - require(_msgSender() == scriptTrigger, "must be scriptTrigger"); - DerivativeContract(callerToDerivativeAddress[addr]).withdrawNativeToken( - _amount - ); - ICommonOFT.LzCallParams memory callParams = ICommonOFT.LzCallParams( - payable(_msgSender()), - address(0), - _adapterParams - ); - bytes32 toAddress = bytes32(uint256(uint160(addr))); - (uint256 estimateFee, ) = IOFTWithFee(astrNativeOFT).estimateSendFee( - destChainId, - toAddress, - _amount, - false, - _adapterParams - ); - require(msg.value >= estimateFee, "too small fee"); - if (msg.value != estimateFee) { - uint256 refundAmount = msg.value - estimateFee; - (bool success, ) = _msgSender().call{value: refundAmount}(""); - require(success, "failed to refund"); - } - IOFTWithFee(astrNativeOFT).sendFrom{value: _amount + estimateFee}( - address(this), - destChainId, - toAddress, - _amount, - _minAmount, - callParams - ); - } - - function setDerivativeAddress(address addr) public { - require( - callerToDerivativeAddress[addr] == address(0), - "already set derivativeAddress" - ); - bytes memory bytecode = type(DerivativeContract).creationCode; - bytes32 salt = bytes32(uint256(uint160(addr))); - address derivativeAddress = Create2.deploy(0, salt, bytecode); - callerToDerivativeAddress[addr] = derivativeAddress; - isDerivativeAddress[derivativeAddress] = true; - emit SetDerivativeAddress(addr, derivativeAddress); - } - - function setLayerZeroFee() external { - require(_msgSender() == scriptTrigger, "must be scriptTrigger"); - bytes32 toAddress = bytes32(uint256(uint160(scriptTrigger))); - uint256 amount = 1000 ether; - bytes memory adapterParams = abi.encodePacked( - uint16(1), - uint256(100000) - ); - - (uint256 vastrFee, ) = IOFTV2(vAstrProxyOFT).estimateSendFee( - destChainId, - toAddress, - amount, - false, - adapterParams - ); - - (uint256 astrFee, ) = IOFTWithFee(astrNativeOFT).estimateSendFee( - destChainId, - toAddress, - amount, - false, - adapterParams - ); - - astrLayerZeroFee = astrFee; - vastrLayerZeroFee = vastrFee; - - emit SetLayerZeroFee( - scriptTrigger, - astrLayerZeroFee, - vastrLayerZeroFee - ); - } - - function setScriptTrigger(address _scriptTrigger) external onlyOwner { - require(_scriptTrigger != address(0), "invalid address"); - scriptTrigger = _scriptTrigger; - emit SetScriptTrigger(_scriptTrigger); - } - - function xcmTransferNativeAsset(address to, uint256 amount) internal { - bytes32 publicKey = AddressToAccount.AddressToSubstrateAccount(to); - address[] memory assetId = new address[](1); - uint256[] memory assetAmount = new uint256[](1); - assetId[0] = address(0); - assetAmount[0] = amount; - require( - XCM(polkadotXcm).assets_reserve_transfer( - assetId, - assetAmount, - publicKey, - IS_RELAY_CHAIN, - BIFROST_PARA_ID, - 0 - ), - "Failed to send xcm" - ); - } - - function xcmTransferAsset( - address assetAddress, - address to, - uint256 amount - ) internal { - require(assetAddress != address(0), "Invalid assetAddress"); - bytes32 publicKey = AddressToAccount.AddressToSubstrateAccount(to); - - address[] memory assetId = new address[](1); - uint256[] memory assetAmount = new uint256[](1); - assetId[0] = assetAddress; - assetAmount[0] = amount; - require( - XCM(polkadotXcm).assets_withdraw( - assetId, - assetAmount, - publicKey, - IS_RELAY_CHAIN, - BIFROST_PARA_ID, - 0 - ), - "Failed to send xcm" - ); - } - - receive() external payable { - require( - isDerivativeAddress[_msgSender()] || _msgSender() == astrNativeOFT, - "sender is not a derivativeAddress or astrNativeOFT" - ); - emit Receive(_msgSender(), msg.value); - } -} diff --git a/src/SlpxContracts/AstarSlpx.sol b/src/SlpxContracts/AstarSlpx.sol deleted file mode 100644 index c68852f..0000000 --- a/src/SlpxContracts/AstarSlpx.sol +++ /dev/null @@ -1,465 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; - -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; -import "./interfaces/XCM.sol"; -import "./interfaces/XCM_v2.sol"; -import "./interfaces/ISlpx.sol"; -import "./utils/BuildCallData.sol"; -import "./utils/AddressToAccount.sol"; - -contract AstarSlpx is ISlpx, OwnableUpgradeable, PausableUpgradeable { - address private constant NATIVE_ASSET_ADDRESS = - 0x0000000000000000000000000000000000000000; - address private constant BNC_ADDRESS = - 0xfFffFffF00000000000000010000000000000007; - address private constant XCM_ADDRESS = - 0x0000000000000000000000000000000000005004; - bytes1 private constant ASTAR_CHAIN = 0x00; - uint32 private constant BIFROST_PARA_ID = 2030; - - enum Operation { - Mint, - Redeem, - ZenlinkSwap, - StableSwap - } - - struct AssetInfo { - bytes2 currencyId; - uint256 operationalMin; - } - - struct FeeInfo { - uint64 transactRequiredWeightAtMost; - uint256 feeAmount; - } - - mapping(address => AssetInfo) public addressToAssetInfo; - mapping(Operation => FeeInfo) public operationToFeeInfo; - struct DestChainInfo { - bool is_evm; - bool is_substrate; - bytes1 raw_chain_index; - } - mapping(uint64 => DestChainInfo) public destChainInfo; - - function checkAssetIsExist( - address assetAddress - ) internal view returns (bytes2) { - AssetInfo memory assetInfo = addressToAssetInfo[assetAddress]; - require(assetInfo.operationalMin > 0, "Asset is not exist"); - require(assetInfo.currencyId != bytes2(0), "Invalid asset"); - return assetInfo.currencyId; - } - - function checkFeeInfo( - Operation operation - ) internal view returns (FeeInfo memory) { - FeeInfo memory feeInfo = operationToFeeInfo[operation]; - require( - feeInfo.transactRequiredWeightAtMost > 0, - "Invalid transactRequiredWeightAtMost" - ); - require(feeInfo.feeAmount > 0, "Invalid feeAmount"); - return feeInfo; - } - - function initialize() public initializer { - __Ownable_init(); - __Pausable_init(); - setAssetAddressInfo(NATIVE_ASSET_ADDRESS, 0x0803, 1000000000000000000); - } - - function setOperationToFeeInfo( - Operation _operation, - uint64 _transactRequiredWeightAtMost, - uint256 _feeAmount - ) public onlyOwner { - require( - _transactRequiredWeightAtMost <= 10000000000, - "transactRequiredWeightAtMost too large" - ); - require(_feeAmount <= 1000000000000, "feeAmount too large"); - operationToFeeInfo[_operation] = FeeInfo( - _transactRequiredWeightAtMost, - _feeAmount - ); - } - - function setAssetAddressInfo( - address assetAddress, - bytes2 currencyId, - uint256 minimumValue - ) public onlyOwner { - require(minimumValue != 0, "Invalid minimumValue"); - require(currencyId != bytes2(0), "Invalid currencyId"); - AssetInfo storage assetInfo = addressToAssetInfo[assetAddress]; - assetInfo.currencyId = currencyId; - assetInfo.operationalMin = minimumValue; - } - - function pause() external onlyOwner { - _pause(); - } - - function unpause() external onlyOwner { - _unpause(); - } - - function xcmTransferNativeAsset(uint256 amount) internal { - require( - amount >= addressToAssetInfo[NATIVE_ASSET_ADDRESS].operationalMin, - "Less than MinimumValue" - ); - bytes32 publicKey = AddressToAccount.AddressToSubstrateAccount( - _msgSender() - ); - address[] memory assetId = new address[](1); - uint256[] memory assetAmount = new uint256[](1); - assetId[0] = NATIVE_ASSET_ADDRESS; - assetAmount[0] = amount; - require( - XCM(XCM_ADDRESS).assets_reserve_transfer( - assetId, - assetAmount, - publicKey, - false, - BIFROST_PARA_ID, - 0 - ), - "Failed to send xcm" - ); - } - - function xcmTransferAsset(address assetAddress, uint256 amount) internal { - require(assetAddress != address(0), "Invalid assetAddress"); - require( - amount >= addressToAssetInfo[assetAddress].operationalMin, - "Less than MinimumValue" - ); - bytes32 publicKey = AddressToAccount.AddressToSubstrateAccount( - _msgSender() - ); - - XCM_v2.Multilocation memory dest_account = getXtokensDestination( - publicKey - ); - IERC20 asset = IERC20(assetAddress); - asset.transferFrom(_msgSender(), address(this), amount); - require( - XCM_v2(XCM_ADDRESS).transfer( - assetAddress, - amount, - dest_account, - XCM_v2.WeightV2(0, 0) - ), - "Failed to send xcm" - ); - } - - function mintVNativeAsset( - address receiver, - string memory remark - ) external payable override whenNotPaused { - require(bytes(remark).length <= 32, "remark too long"); - bytes2 nativeToken = checkAssetIsExist(NATIVE_ASSET_ADDRESS); - - xcmTransferNativeAsset(msg.value); - - bytes memory targetChain = abi.encodePacked(ASTAR_CHAIN, receiver); - bytes memory callData = BuildCallData.buildMintCallBytes( - _msgSender(), - nativeToken, - targetChain, - remark - ); - - // xcm transact - FeeInfo memory feeInfo = checkFeeInfo(Operation.Mint); - require( - XCM(XCM_ADDRESS).remote_transact( - BIFROST_PARA_ID, - false, - BNC_ADDRESS, - feeInfo.feeAmount, - callData, - feeInfo.transactRequiredWeightAtMost - ), - "Failed to send xcm" - ); - emit Mint( - _msgSender(), - NATIVE_ASSET_ADDRESS, - msg.value, - receiver, - callData, - remark - ); - } - - function mintVAsset( - address assetAddress, - uint256 amount, - address receiver, - string memory remark - ) external override { - require(bytes(remark).length <= 32, "remark too long"); - - bytes2 token = checkAssetIsExist(assetAddress); - - // xtokens call - xcmTransferAsset(assetAddress, amount); - - bytes memory targetChain = abi.encodePacked(ASTAR_CHAIN, receiver); - bytes memory callData = BuildCallData.buildMintCallBytes( - _msgSender(), - token, - targetChain, - remark - ); - - // xcm transact - FeeInfo memory feeInfo = checkFeeInfo(Operation.Mint); - require( - XCM(XCM_ADDRESS).remote_transact( - BIFROST_PARA_ID, - false, - BNC_ADDRESS, - feeInfo.feeAmount, - callData, - feeInfo.transactRequiredWeightAtMost - ), - "Failed to send xcm" - ); - emit Mint( - _msgSender(), - assetAddress, - amount, - receiver, - callData, - remark - ); - } - - function mintVNativeAssetWithChannelId( - address receiver, - string memory remark, - uint32 channel_id - ) external payable override whenNotPaused { - require(bytes(remark).length <= 32, "remark too long"); - bytes2 nativeToken = checkAssetIsExist(NATIVE_ASSET_ADDRESS); - - xcmTransferNativeAsset(msg.value); - - bytes memory targetChain = abi.encodePacked(ASTAR_CHAIN, receiver); - bytes memory callData = BuildCallData.buildMintWithChannelIdCallBytes( - _msgSender(), - nativeToken, - targetChain, - remark, - channel_id - ); - - // xcm transact - FeeInfo memory feeInfo = checkFeeInfo(Operation.Mint); - require( - XCM(XCM_ADDRESS).remote_transact( - BIFROST_PARA_ID, - false, - BNC_ADDRESS, - feeInfo.feeAmount, - callData, - feeInfo.transactRequiredWeightAtMost - ), - "Failed to send xcm" - ); - emit Mint( - _msgSender(), - NATIVE_ASSET_ADDRESS, - msg.value, - receiver, - callData, - remark - ); - } - - function mintVAssetWithChannelId( - address assetAddress, - uint256 amount, - address receiver, - string memory remark, - uint32 channel_id - ) external override { - require(bytes(remark).length <= 32, "remark too long"); - - bytes2 token = checkAssetIsExist(assetAddress); - - // xtokens call - xcmTransferAsset(assetAddress, amount); - - bytes memory targetChain = abi.encodePacked(ASTAR_CHAIN, receiver); - bytes memory callData = BuildCallData.buildMintWithChannelIdCallBytes( - _msgSender(), - token, - targetChain, - remark, - channel_id - ); - - // xcm transact - FeeInfo memory feeInfo = checkFeeInfo(Operation.Mint); - require( - XCM(XCM_ADDRESS).remote_transact( - BIFROST_PARA_ID, - false, - BNC_ADDRESS, - feeInfo.feeAmount, - callData, - feeInfo.transactRequiredWeightAtMost - ), - "Failed to send xcm" - ); - emit Mint( - _msgSender(), - assetAddress, - amount, - receiver, - callData, - remark - ); - } - - function redeemAsset( - address vAssetAddress, - uint256 amount, - address receiver - ) external override whenNotPaused { - bytes2 vtoken = checkAssetIsExist(vAssetAddress); - - xcmTransferAsset(vAssetAddress, amount); - - bytes memory targetChain = abi.encodePacked(ASTAR_CHAIN, receiver); - bytes memory callData = BuildCallData.buildRedeemCallBytes( - _msgSender(), - vtoken, - targetChain - ); - // xcm transact - FeeInfo memory feeInfo = checkFeeInfo(Operation.Redeem); - require( - XCM(XCM_ADDRESS).remote_transact( - BIFROST_PARA_ID, - false, - BNC_ADDRESS, - feeInfo.feeAmount, - callData, - feeInfo.transactRequiredWeightAtMost - ), - "Failed to send xcm" - ); - emit Redeem(_msgSender(), vAssetAddress, amount, receiver, callData); - } - - function setDestChainInfo( - uint64 dest_chain_id, - bool is_evm, - bool is_substrate, - bytes1 raw_chain_index - ) public onlyOwner { - require( - !(is_evm && is_substrate), - "Both is_evm and is_substrate cannot be true" - ); - DestChainInfo storage chainInfo = destChainInfo[dest_chain_id]; - chainInfo.is_evm = is_evm; - chainInfo.is_substrate = is_substrate; - chainInfo.raw_chain_index = raw_chain_index; - } - - function create_order( - address assetAddress, - uint128 amount, - uint64 dest_chain_id, - bytes memory receiver, - string memory remark, - uint32 channel_id - ) external payable override { - require( - bytes(remark).length > 0 && bytes(remark).length <= 32, - "remark must be less than 32 bytes and not empty" - ); - require(amount > 0, "amount must be greater than 0"); - - DestChainInfo memory chainInfo = destChainInfo[dest_chain_id]; - if (chainInfo.is_evm) { - require(receiver.length == 20, "evm address must be 20 bytes"); - } else if (chainInfo.is_substrate) { - require( - receiver.length == 32, - "substrate public key must be 32 bytes" - ); - } else { - revert("Destination chain is not supported"); - } - - bytes2 token = checkAssetIsExist(assetAddress); - if (assetAddress == NATIVE_ASSET_ADDRESS) { - amount = uint128(msg.value); - xcmTransferNativeAsset(uint256(amount)); - } else { - xcmTransferAsset(assetAddress, uint256(amount)); - } - - // Build bifrost slpx create order call data - bytes memory callData = BuildCallData.buildCreateOrderCallBytes( - _msgSender(), - block.chainid, - block.number, - token, - amount, - abi.encodePacked(chainInfo.raw_chain_index, receiver), - remark, - channel_id - ); - // xcm transact - FeeInfo memory feeInfo = checkFeeInfo(Operation.Mint); - require( - XCM(XCM_ADDRESS).remote_transact( - BIFROST_PARA_ID, - false, - BNC_ADDRESS, - feeInfo.feeAmount, - callData, - feeInfo.transactRequiredWeightAtMost - ), - "Failed to send xcm" - ); - emit CreateOrder( - assetAddress, - amount, - dest_chain_id, - receiver, - remark, - channel_id - ); - } - - function getXtokensDestination( - bytes32 publicKey - ) internal pure returns (XCM_v2.Multilocation memory) { - bytes[] memory interior = new bytes[](2); - // Parachain: 2001/2030 - interior[0] = bytes.concat(hex"00", bytes4(BIFROST_PARA_ID)); - // AccountId32: { id: public_key , network: any } - interior[1] = bytes.concat(hex"01", publicKey, hex"00"); - XCM_v2.Multilocation memory dest = XCM_v2.Multilocation({ - parents: 1, - interior: interior - }); - - return dest; - } -} diff --git a/src/SlpxContracts/AstarZkSlpx.sol b/src/SlpxContracts/AstarZkSlpx.sol deleted file mode 100644 index 1bd9f71..0000000 --- a/src/SlpxContracts/AstarZkSlpx.sol +++ /dev/null @@ -1,152 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; - -import "@openzeppelin/contracts/access/Ownable.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "./interfaces/IOFTWithFee.sol"; -import "./interfaces/IOFTV2.sol"; -import "./interfaces/Types.sol"; - -contract AstarZkSlpx is Ownable { - address public constant astrOFTWithFee = - 0xdf41220C7e322bFEF933D85D01821ad277f90172; - address public constant vAstrOFT = - 0x7746ef546d562b443AE4B4145541a3b1a3D75717; - uint16 public constant destChainId = 210; - bytes32 public remoteContract; - - mapping(Types.Operation => uint256) public minAmount; - - event Mint(address indexed caller, uint256 indexed amount); - event Redeem(address indexed caller, uint256 indexed amount); - - function setRemoteContract(address _remoteContract) public onlyOwner { - require(_remoteContract != address(0), "Invalid remoteContract"); - remoteContract = bytes32(uint256(uint160(_remoteContract))); - } - - function setMinAmount( - Types.Operation _operation, - uint256 _minAmount - ) public onlyOwner { - require(_minAmount != 0, "Invalid minAmount"); - minAmount[_operation] = _minAmount; - } - - function mint( - uint256 _amount, - uint64 _dstGasForCall, - bytes calldata _adapterParams - ) external payable { - require(_amount >= minAmount[Types.Operation.Mint], "amount too small"); - ICommonOFT.LzCallParams memory callParams = ICommonOFT.LzCallParams( - payable(_msgSender()), - address(0), - _adapterParams - ); - - (uint256 estimateFee, bytes memory payload) = estimateSendAndCallFee( - _msgSender(), - Types.Operation.Mint, - _amount, - _dstGasForCall, - _adapterParams - ); - require(msg.value >= estimateFee, "too small fee"); - if (msg.value != estimateFee) { - uint256 refundAmount = msg.value - estimateFee; - (bool success, ) = _msgSender().call{value: refundAmount}(""); - require(success, "failed to refund"); - } - - IOFTWithFee(astrOFTWithFee).sendAndCall{value: estimateFee}( - _msgSender(), - destChainId, - remoteContract, - _amount, - _amount / 2, - payload, - _dstGasForCall, - callParams - ); - - emit Mint(_msgSender(), _amount); - } - - function redeem( - uint256 _amount, - uint64 _dstGasForCall, - bytes calldata _adapterParams - ) external payable { - require( - _amount >= minAmount[Types.Operation.Redeem], - "amount too small" - ); - ICommonOFT.LzCallParams memory callParams = ICommonOFT.LzCallParams( - payable(_msgSender()), - address(0), - _adapterParams - ); - - (uint256 estimateFee, bytes memory payload) = estimateSendAndCallFee( - _msgSender(), - Types.Operation.Redeem, - _amount, - _dstGasForCall, - _adapterParams - ); - require(msg.value >= estimateFee, "too small fee"); - if (msg.value != estimateFee) { - uint256 refundAmount = msg.value - estimateFee; - (bool success, ) = _msgSender().call{value: refundAmount}(""); - require(success, "failed to refund"); - } - - IOFTV2(vAstrOFT).sendAndCall{value: estimateFee}( - _msgSender(), - destChainId, - remoteContract, - _amount, - payload, - _dstGasForCall, - callParams - ); - - emit Redeem(_msgSender(), _amount); - } - - function estimateSendAndCallFee( - address caller, - Types.Operation operation, - uint256 _amount, - uint64 _dstGasForCall, - bytes calldata _adapterParams - ) public view returns (uint256, bytes memory) { - if (operation == Types.Operation.Mint) { - bytes memory payload = abi.encode(caller, Types.Operation.Mint); - (uint256 estimateFee, ) = IOFTWithFee(astrOFTWithFee) - .estimateSendAndCallFee( - destChainId, - remoteContract, - _amount, - payload, - _dstGasForCall, - false, - _adapterParams - ); - return (estimateFee, payload); - } else { - bytes memory payload = abi.encode(caller, Types.Operation.Redeem); - (uint256 estimateFee, ) = IOFTV2(vAstrOFT).estimateSendAndCallFee( - destChainId, - remoteContract, - _amount, - payload, - _dstGasForCall, - false, - _adapterParams - ); - return (estimateFee, payload); - } - } -} diff --git a/src/SlpxContracts/DerivativeContract.sol b/src/SlpxContracts/DerivativeContract.sol deleted file mode 100644 index af66395..0000000 --- a/src/SlpxContracts/DerivativeContract.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; - -import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - -contract DerivativeContract is ReentrancyGuard { - address public receiver; - - event Withdraw(address caller, address to, address token, uint256 amount); - - constructor() { - receiver = msg.sender; - } - - function withdrawErc20Token( - address _erc20 - ) external nonReentrant returns (uint256) { - require(msg.sender == receiver, "forbidden"); - require(_erc20 != address(0), "invalid erc20"); - uint256 balance = IERC20(_erc20).balanceOf(address(this)); - require(balance != 0, "balance to low"); - IERC20(_erc20).transfer(receiver, balance); - emit Withdraw(msg.sender, receiver, _erc20, balance); - return balance; - } - - function withdrawNativeToken(uint256 _amount) external nonReentrant { - require(msg.sender == receiver, "forbidden"); - require(_amount != 0, "balance to low"); - (bool success, ) = receiver.call{value: _amount}(""); - require(success, "failed to withdrawNativeToken"); - emit Withdraw(msg.sender, receiver, address(0), _amount); - } -} diff --git a/src/SlpxContracts/EthereumSlpx.sol b/src/SlpxContracts/EthereumSlpx.sol index efa46ab..5fa4e24 100644 --- a/src/SlpxContracts/EthereumSlpx.sol +++ b/src/SlpxContracts/EthereumSlpx.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; +pragma solidity ^0.8.28; import "./interfaces/IVETH.sol"; import "./interfaces/snowbridge/IGateway.sol"; diff --git a/src/SlpxContracts/MantaPacificSlpx.sol b/src/SlpxContracts/MantaPacificSlpx.sol index be46746..5283a4d 100644 --- a/src/SlpxContracts/MantaPacificSlpx.sol +++ b/src/SlpxContracts/MantaPacificSlpx.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; +pragma solidity ^0.8.28; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/src/SlpxContracts/MoonbeamReceiver.sol b/src/SlpxContracts/MoonbeamReceiver.sol index dc1c9f2..12b69f3 100644 --- a/src/SlpxContracts/MoonbeamReceiver.sol +++ b/src/SlpxContracts/MoonbeamReceiver.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; +pragma solidity ^0.8.28; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/src/SlpxContracts/MoonbeamSlpx.sol b/src/SlpxContracts/MoonbeamSlpx.sol index 1f3bbd1..84d7949 100644 --- a/src/SlpxContracts/MoonbeamSlpx.sol +++ b/src/SlpxContracts/MoonbeamSlpx.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; +pragma solidity ^0.8.28; import "./interfaces/XcmTransactorV2.sol"; import "./interfaces/Xtokens.sol"; diff --git a/src/SlpxContracts/XcmOracle.sol b/src/SlpxContracts/XcmOracle.sol index 6043ad2..19cd211 100644 --- a/src/SlpxContracts/XcmOracle.sol +++ b/src/SlpxContracts/XcmOracle.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; +pragma solidity ^0.8.28; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; diff --git a/src/SlpxContracts/interfaces/ICommonOFT.sol b/src/SlpxContracts/interfaces/ICommonOFT.sol index 0d4423d..424e20c 100644 --- a/src/SlpxContracts/interfaces/ICommonOFT.sol +++ b/src/SlpxContracts/interfaces/ICommonOFT.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; +pragma solidity ^0.8.28; import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; diff --git a/src/SlpxContracts/interfaces/IOFTReceiverV2.sol b/src/SlpxContracts/interfaces/IOFTReceiverV2.sol index 93e60e7..0167cb9 100644 --- a/src/SlpxContracts/interfaces/IOFTReceiverV2.sol +++ b/src/SlpxContracts/interfaces/IOFTReceiverV2.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; +pragma solidity ^0.8.28; interface IOFTReceiverV2 { /** diff --git a/src/SlpxContracts/interfaces/IOFTV2.sol b/src/SlpxContracts/interfaces/IOFTV2.sol index b29e3e4..a980200 100644 --- a/src/SlpxContracts/interfaces/IOFTV2.sol +++ b/src/SlpxContracts/interfaces/IOFTV2.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; +pragma solidity ^0.8.28; import "./ICommonOFT.sol"; diff --git a/src/SlpxContracts/interfaces/IOFTWithFee.sol b/src/SlpxContracts/interfaces/IOFTWithFee.sol index efc96df..23cdefb 100644 --- a/src/SlpxContracts/interfaces/IOFTWithFee.sol +++ b/src/SlpxContracts/interfaces/IOFTWithFee.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; +pragma solidity ^0.8.28; import "./ICommonOFT.sol"; diff --git a/src/SlpxContracts/interfaces/ISlpx.sol b/src/SlpxContracts/interfaces/ISlpx.sol index e448a3e..a429fa5 100644 --- a/src/SlpxContracts/interfaces/ISlpx.sol +++ b/src/SlpxContracts/interfaces/ISlpx.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; +pragma solidity ^0.8.28; interface ISlpx { event Mint( diff --git a/src/SlpxContracts/interfaces/Types.sol b/src/SlpxContracts/interfaces/Types.sol index b972d7e..7ca0ba4 100644 --- a/src/SlpxContracts/interfaces/Types.sol +++ b/src/SlpxContracts/interfaces/Types.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; +pragma solidity ^0.8.28; interface Types { enum Operation { diff --git a/src/SlpxContracts/interfaces/XCM.sol b/src/SlpxContracts/interfaces/XCM.sol index 181b188..9eeafeb 100644 --- a/src/SlpxContracts/interfaces/XCM.sol +++ b/src/SlpxContracts/interfaces/XCM.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; +pragma solidity ^0.8.28; /** * @title XCM interface. diff --git a/src/SlpxContracts/interfaces/Xtokens.sol b/src/SlpxContracts/interfaces/Xtokens.sol index 1c5790a..e570ea7 100644 --- a/src/SlpxContracts/interfaces/Xtokens.sol +++ b/src/SlpxContracts/interfaces/Xtokens.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; +pragma solidity ^0.8.28; /// @dev The Xtokens contract's address. address constant XTOKENS_ADDRESS = 0x0000000000000000000000000000000000000804; diff --git a/src/SlpxContracts/interfaces/snowbridge/IGateway.sol b/src/SlpxContracts/interfaces/snowbridge/IGateway.sol deleted file mode 100644 index 7d98d98..0000000 --- a/src/SlpxContracts/interfaces/snowbridge/IGateway.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2023 Snowfork -pragma solidity 0.8.10; - -import "./MultiAddress.sol"; - -interface IGateway { - /// @dev Quote a fee in Ether for sending a token - /// 1. Delivery costs to BridgeHub - /// 2. XCM execution costs on destinationChain - function quoteSendTokenFee(address token, uint32 destinationChain, uint128 destinationFee) - external - view - returns (uint256); - - /// @dev Send ERC20 tokens to parachain `destinationChain` and deposit into account `destinationAddress` - function sendToken( - address token, - uint32 destinationChain, - MultiAddress.MultiAddress calldata destinationAddress, - uint128 destinationFee, - uint128 amount - ) external payable; -} \ No newline at end of file diff --git a/src/SlpxContracts/interfaces/snowbridge/MultiAddress.sol b/src/SlpxContracts/interfaces/snowbridge/MultiAddress.sol deleted file mode 100644 index 4895ad8..0000000 --- a/src/SlpxContracts/interfaces/snowbridge/MultiAddress.sol +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2023 Snowfork -pragma solidity 0.8.10; - -contract MultiAddress { - /// @dev An address for an on-chain account - struct MultiAddress { - Kind kind; - bytes data; - } - - enum Kind { - Index, - Address32, - Address20 - } - - function isIndex(MultiAddress calldata multiAddress) public pure returns (bool) { - return multiAddress.kind == Kind.Index; - } - - function asIndex(MultiAddress calldata multiAddress) public pure returns (uint32) { - return abi.decode(multiAddress.data, (uint32)); - } - - function isAddress32(MultiAddress calldata multiAddress) public pure returns (bool) { - return multiAddress.kind == Kind.Address32; - } - - function asAddress32(MultiAddress calldata multiAddress) public pure returns (bytes32) { - return bytes32(multiAddress.data); - } - - function isAddress20(MultiAddress calldata multiAddress) public pure returns (bool) { - return multiAddress.kind == Kind.Address20; - } - - function asAddress20(MultiAddress calldata multiAddress) public pure returns (bytes20) { - return bytes20(multiAddress.data); - } - - function multiAddressFromUint32(uint32 id) public pure returns (MultiAddress memory) { - return MultiAddress({kind: Kind.Index, data: abi.encode(id)}); - } - - function multiAddressFromBytes32(bytes32 id) public pure returns (MultiAddress memory) { - return MultiAddress({kind: Kind.Address32, data: bytes.concat(id)}); - } - - function multiAddressFromBytes20(bytes20 id) public pure returns (MultiAddress memory) { - return MultiAddress({kind: Kind.Address20, data: bytes.concat(id)}); - } -} \ No newline at end of file diff --git a/src/SlpxContracts/utils/AddressToAccount.sol b/src/SlpxContracts/utils/AddressToAccount.sol index 2cb5409..7d6b297 100644 --- a/src/SlpxContracts/utils/AddressToAccount.sol +++ b/src/SlpxContracts/utils/AddressToAccount.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; +pragma solidity ^0.8.28; import "./Blake2b.sol"; diff --git a/src/SlpxContracts/utils/Blake2b.sol b/src/SlpxContracts/utils/Blake2b.sol index 74e0dee..628d44f 100644 --- a/src/SlpxContracts/utils/Blake2b.sol +++ b/src/SlpxContracts/utils/Blake2b.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; +pragma solidity ^0.8.28; library Blake2b { struct Instance { diff --git a/src/SlpxContracts/utils/BuildCallData.sol b/src/SlpxContracts/utils/BuildCallData.sol index de64f8c..40dc946 100644 --- a/src/SlpxContracts/utils/BuildCallData.sol +++ b/src/SlpxContracts/utils/BuildCallData.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.10; +pragma solidity ^0.8.28; library BuildCallData { uint8 public constant PALLET_INDEX = 125;