Commit b40aa685 authored by luxq's avatar luxq

add new scripts

parent def735e7
// SPDX-License-Identifier: MIT
// Created by Caduceus.foundation
pragma solidity ^0.8.20;
interface IERC20Token {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20Token {
using Address for address;
function safeTransfer(
IERC20Token token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20Token token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20Token token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20Token token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20Token token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20Token token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
contract HandoutContract {
using SafeERC20Token for IERC20Token;
address private _owner;
mapping(address=>bool) _admins;
modifier onlyOwner() {
require(msg.sender == _owner, "Only owner can do it");
_;
}
modifier onlyAdmin() {
require(_admins[msg.sender], "Only admin can do it");
_;
}
event Transfer(address indexed src, address indexed dst, uint value);
constructor() {
_owner = msg.sender;
_admins[msg.sender] = true;
}
receive() external payable {
}
function getAdmin(address addr) public view returns (bool) {
return _admins[addr];
}
function setAdmin(address[] memory lst, bool b) public onlyOwner() {
for (uint i = 0; i < lst.length; i++) {
_admins[lst[i]] = b;
}
}
function Handout(address[] memory to, uint v) public onlyAdmin {
uint value = v * 10 ** 18;
for (uint i = 0; i < to.length; i++) {
require(address(this).balance >= value);
payable(to[i]).transfer(value);
emit Transfer(address(this), to[i], value);
}
}
function Handout(address[] memory to, uint v, uint d) public onlyAdmin {
require(d <= 18, "Decimals must be less than 18");
uint value = v * 10 ** (18 - d);
for (uint i = 0; i < to.length; i++) {
require(address(this).balance >= value);
payable(to[i]).transfer(value);
emit Transfer(address(this), to[i], value);
}
}
function Handout(address[] memory to, uint[] memory v) public onlyAdmin {
for (uint i = 0; i < to.length; i++) {
uint value = v[i] * 10 ** 18;
require(address(this).balance >= value);
payable(to[i]).transfer(value);
emit Transfer(address(this), to[i], value);
}
}
function Handout(address[] memory to, uint[] memory v, uint d) public onlyAdmin {
require(d <= 18, "Decimals must be less than 18");
for (uint i = 0; i < to.length; i++) {
uint value = v[i] * 10 ** (18 - d);
require(address(this).balance >= value);
payable(to[i]).transfer(value);
emit Transfer(address(this), to[i], value);
}
}
function Handout(address token, address[] memory to, uint v) public onlyAdmin {
uint value = v * 10 ** 18;
for (uint i = 0; i < to.length; i++) {
require(IERC20Token(token).balanceOf(address(this)) >= value);
IERC20Token(token).safeTransfer(to[i], value);
}
}
function Handout(address token, address[] memory to, uint v, uint d) public onlyAdmin {
require(d <= 18, "Decimals must be less than 18");
uint value = v * 10 ** (18 - d);
for (uint i = 0; i < to.length; i++) {
require(IERC20Token(token).balanceOf(address(this)) >= value);
IERC20Token(token).safeTransfer(to[i], value);
}
}
function Handout(address token, address[] memory to, uint[] memory v) public onlyAdmin {
for (uint i = 0; i < to.length; i++) {
uint value = v[i] * 10 ** 18;
require(IERC20Token(token).balanceOf(address(this)) >= value);
IERC20Token(token).safeTransfer(to[i], value);
}
}
function Handout(address token, address[] memory to, uint[] memory v, uint d) public onlyOwner {
for (uint i = 0; i < to.length; i++) {
uint value = v[i] * 10 ** (18 - d);
require(IERC20Token(token).balanceOf(address(this)) >= value);
IERC20Token(token).safeTransfer(to[i], value);
}
}
}
\ No newline at end of file
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
contract WMDD {
string public name = "Wrapped MMDD";
string public symbol = "MMDD";
uint8 public decimals = 18;
event Approval(address indexed src, address indexed guy, uint wad);
event Transfer(address indexed src, address indexed dst, uint wad);
event Deposit(address indexed dst, uint wad);
event Withdrawal(address indexed src, uint wad);
mapping(address => uint) public balanceOf;
mapping(address => mapping(address => uint)) public allowance;
// 接收以太币
receive() external payable {
deposit();
}
function deposit() public payable {
balanceOf[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value);
}
function withdraw(uint wad) public {
require(balanceOf[msg.sender] >= wad, "Insufficient balance");
balanceOf[msg.sender] -= wad;
// 使用 call 发送以太币
(bool success, ) = msg.sender.call{value: wad}("");
require(success, "Transfer failed");
emit Withdrawal(msg.sender, wad);
}
function totalSupply() public view returns (uint) {
return address(this).balance;
}
function approve(address guy, uint wad) public returns (bool) {
allowance[msg.sender][guy] = wad;
emit Approval(msg.sender, guy, wad);
return true;
}
function transfer(address dst, uint wad) public returns (bool) {
return transferFrom(msg.sender, dst, wad);
}
function transferFrom(address src, address dst, uint wad) public returns (bool) {
require(balanceOf[src] >= wad, "Insufficient balance");
if (src != msg.sender && allowance[src][msg.sender] != type(uint).max) {
require(allowance[src][msg.sender] >= wad, "Allowance exceeded");
allowance[src][msg.sender] -= wad;
}
balanceOf[src] -= wad;
balanceOf[dst] += wad;
emit Transfer(src, dst, wad);
return true;
}
}
\ No newline at end of file
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title IERC20Burnable
* @dev 扩展ERC20接口,添加销毁功能
*/
interface IERC20Burnable is IERC20 {
function burn(uint256 amount) external;
}
/**
* @title Bridge
* @dev 跨链桥合约,支持代币在不同区块链之间转移
* @notice 使用多签验证者机制确保跨链转移的安全性
*/
contract Bridge is Ownable {
/// @dev 执行转入操作所需的最少验证者确认数量
uint256 public validRequired;
/// @dev 记录地址是否为验证者
mapping(address => bool) public isValidator;
/**
* @dev 转出配置结构体
* @param receiveToken 在目标链上接收的代币合约地址
* @param fee 转移手续费
* @param limit 单次转移的最大金额限制
* @param isBurn 是否销毁源链上的代币(用于总供应量固定的代币)
* @param enabled 是否启用该转移路径
*/
struct OutConfig {
address receiveToken;
uint256 fee;
uint256 limit;
bool isBurn;
bool enabled;
}
/// @dev 转出配置映射:源链代币地址 => 目标链ID => 转出配置
mapping(address => mapping(uint256 => OutConfig)) public outConfiguration;
/// @dev 获取支持转出的代币列表:目标链ID => 代币地址数组
mapping(uint256 => address[]) public supportedTokensOut;
/// @dev 检查代币是否支持转出:目标链ID => 代币地址 => 是否支持
mapping(uint256 => mapping(address => bool)) public isTokenSupportedOut;
/// @dev 转出记录映射:转出ID => 转出信息
mapping(uint256 => OutInfo) public outTransfers;
/// @dev 转入记录映射:转入ID => 转入信息
mapping(uint256 => InInfo) public inTransfers;
/// @dev 获取转入ID映射:源链ID => 转出ID => 转入ID
mapping(uint256 => mapping(uint256 => uint256)) public getInId;
/// @dev 转出操作的自增ID计数器
uint256 public _outID;
/// @dev 转入操作的自增ID计数器
uint256 public _inID;
/// @dev 每种代币的累计转出总量
mapping(address => uint256) public outTotal;
/// @dev 每种代币的累计转入总量
mapping(address => uint256) public inTotal;
/// @dev 金库地址
address public treasury;
/**
* @dev 代币金库配置结构体
* @param minReserve 最小保留金额(合约必须保留的最少数量)
* @param reserveRatio 保留比例(基数10000,如5000表示50%)
* @param enabled 是否启用金库转移功能
*/
struct TreasuryConfig {
uint256 minReserve; // 最小保留金额
uint256 reserveRatio; // 保留比例(基数10000)
bool enabled; // 是否启用
}
/// @dev 每个代币的金库配置:代币地址 => 金库配置
mapping(address => TreasuryConfig) public treasuryConfigs;
/// @dev 每个代币转移到金库的累计总量
mapping(address => uint256) public treasuryTotal;
/**
* @dev 转出操作信息结构体
* @param outId 转出操作的唯一ID
* @param fromChainID 源链ID
* @param sender 发送者地址
* @param token 转出的代币合约地址
* @param amount 转出金额
* @param fee 手续费
* @param toChainID 目标链ID
* @param receiver 接收者地址
* @param receiveToken 在目标链上接收的代币合约地址
* @param receiveAmount 实际接收金额(扣除手续费后)
* @param signature 操作签名,用于验证
*/
struct OutInfo {
uint256 outId;
uint256 fromChainID;
address sender;
address token;
uint256 amount;
uint256 fee;
uint256 toChainID;
address receiver;
address receiveToken;
uint256 receiveAmount;
bytes32 signature;
}
/**
* @dev 转入操作信息结构体
* @param inId 转入操作的唯一ID
* @param token 转入的代币合约地址
* @param receiver 接收者地址
* @param fee 手续费
* @param amount 转入金额
* @param fromChainID 源链ID
* @param outId 对应的转出操作ID
* @param executed 是否已执行
* @param confirmCounter 确认数量计数器
* @param confirmations 验证者确认记录
* @param rejectCounter 拒绝数量计数器
* @param rejections 验证者拒绝记录
*/
struct InInfo {
uint256 inId;
address token;
address receiver;
uint256 fee;
uint256 amount;
uint256 fromChainID;
uint256 outId;
bool executed;
uint256 confirmCounter;
mapping(address => bool) confirmations;
uint256 rejectCounter;
mapping(address => bool) rejections;
}
// ============ 事件定义 ============
/**
* @dev 转出操作事件
* @param outId 转出ID
* @param fromChainID 源链ID
* @param sender 发送者
* @param token 代币地址
* @param amount 转出金额
* @param fee 手续费
* @param toChainID 目标链ID
* @param receiver 接收者
* @param receiveToken 接收代币地址
*/
event TransferOut(
uint256 outId,
uint256 fromChainID,
address sender,
address token,
uint256 amount,
uint256 fee,
uint256 toChainID,
address receiver,
address receiveToken
);
/**
* @dev 转入操作事件
* @param fromChainID 源链ID
* @param outId 对应的转出ID
* @param inId 转入ID
* @param receiver 接收者
* @param token 代币地址
* @param amount 转入金额
*/
event TransferIn(
uint256 fromChainID,
uint256 outId,
uint256 inId,
address receiver,
address token,
uint256 amount
);
/**
* @dev 转入确认事件
* @param inId 转入ID
* @param validator 确认的验证者
*/
event TransferInConfirmation(
uint256 inId,
address validator
);
/**
* @dev 转入拒绝事件
* @param inId 转入ID
* @param validator 拒绝的验证者
*/
event TransferInRejection(
uint256 inId,
address validator
);
/**
* @dev 转入执行事件
* @param inId 转入ID
* @param receiver 接收者
* @param token 代币地址
* @param amount 转入金额
*/
event TransferInExecution(
uint256 inId,
address receiver,
address token,
uint256 amount
);
/// @dev 代币配置变更事件
event TokenConfigChanged(address indexed token, uint256 indexed toChainID, bool enabled);
/// @dev 金库地址变更事件
event TreasuryChanged(address indexed oldTreasury, address indexed newTreasury);
/// @dev 金库配置变更事件
event TreasuryConfigChanged(address indexed token, uint256 minReserve, uint256 reserveRatio, bool enabled);
/// @dev 资金转移到金库事件
event TreasuryTransfer(address indexed token, uint256 amount, uint256 contractBalance, uint256 treasuryBalance);
/// @dev 验证者添加事件
event ValidatorAddition(address validator);
/// @dev 验证者移除事件
event ValidatorRemoval(address validator);
/// @dev 所需确认数变更事件
event RequirementChange(uint prior, uint256 present);
// ============ 构造函数 ============
/**
* @notice 初始化合约,设置初始所需确认数为1
* @param initialOwner 合约的初始所有者
*/
constructor(address initialOwner) Ownable(initialOwner) {
validRequired = 1;
treasury = address(0); // 初始化金库地址为0,需要后续设置
}
// ============ 验证者管理函数 ============
/**
* @dev 添加验证者
* @param validator 要添加的验证者地址
* @notice 只有合约所有者可以调用
*/
function addValidator(address validator) external onlyOwner {
isValidator[validator] = true;
emit ValidatorAddition(validator);
}
/**
* @dev 移除验证者
* @param validator 要移除的验证者地址
* @notice 只有合约所有者可以调用
*/
function removeValidator(address validator) external onlyOwner {
isValidator[validator] = false;
emit ValidatorRemoval(validator);
}
/**
* @dev 替换验证者
* @param validator 要被替换的验证者地址
* @param newValidator 新验证者地址
* @notice 只有合约所有者可以调用
* @notice 函数名存在拼写错误,应为replaceValidator
*/
function replaceValidator(address validator, address newValidator) external onlyOwner {
isValidator[validator] = false;
isValidator[newValidator] = true;
emit ValidatorAddition(newValidator);
emit ValidatorRemoval(validator);
}
// ============ 修饰符 ============
/**
* @dev 验证者权限修饰符
* @notice 确保调用者是验证者
*/
modifier onlyValidator {
require(isValidator[msg.sender], "not validator");
_;
}
/**
* @dev 非当前链修饰符
* @param chainID 要检查的链ID
* @notice 确保指定链ID不是当前链
*/
modifier notCurrentChain(uint256 chainID) {
uint256 id;
assembly {
id := chainid()
}
require(chainID != id, "can't current chain");
_;
}
/**
* @dev 当前链修饰符
* @param chainID 要检查的链ID
* @notice 确保指定链ID是当前链
*/
modifier currentChain(uint256 chainID) {
uint256 id;
assembly {
id := chainid()
}
require(chainID == id, "must current chain");
_;
}
// ============ 配置管理函数 ============
/**
* @dev 修改所需验证者确认数量
* @param required 新的所需确认数量
* @notice 只有合约所有者可以调用
*/
function changeValidRequired(uint256 required) external onlyOwner {
uint256 prior = validRequired;
validRequired = required;
emit RequirementChange(prior, required);
}
/**
* @dev 修改转出配置
* @param token 代币合约地址
* @param toChainID 目标链ID
* @param config 新的转出配置
* @notice 只有合约所有者可以调用
* @notice 目标链不能是当前链
*/
function changeOutConfig(address token, uint256 toChainID, OutConfig memory config) external onlyOwner notCurrentChain(toChainID) {
bool wasEnabled = outConfiguration[token][toChainID].enabled;
outConfiguration[token][toChainID] = config;
// 如果是新启用的代币,添加到支持列表
if (config.enabled && !wasEnabled) {
if (!isTokenSupportedOut[toChainID][token]) {
supportedTokensOut[toChainID].push(token);
isTokenSupportedOut[toChainID][token] = true;
}
}
// 如果是禁用代币,从支持列表移除
else if (!config.enabled && wasEnabled) {
_removeTokenFromArray(supportedTokensOut[toChainID], token);
isTokenSupportedOut[toChainID][token] = false;
}
emit TokenConfigChanged(token, toChainID, config.enabled);
}
/**
* @dev 设置金库地址
* @param newTreasury 新的金库地址
* @notice 只有合约所有者可以调用
*/
function setTreasury(address newTreasury) external onlyOwner {
require(newTreasury != address(0), "treasury cannot be zero address");
address oldTreasury = treasury;
treasury = newTreasury;
emit TreasuryChanged(oldTreasury, newTreasury);
}
/**
* @dev 设置代币的金库配置
* @param token 代币地址
* @param minReserve 最小保留金额
* @param reserveRatio 保留比例(基数10000,如5000表示50%)
* @param enabled 是否启用金库功能
* @notice 只有合约所有者可以调用
*/
function setTreasuryConfig(
address token,
uint256 minReserve,
uint256 reserveRatio,
bool enabled
) external onlyOwner {
require(reserveRatio <= 10000, "reserve ratio cannot exceed 100%");
require(minReserve > 0 || !enabled, "min reserve must be positive when enabled");
treasuryConfigs[token] = TreasuryConfig({
minReserve: minReserve,
reserveRatio: reserveRatio,
enabled: enabled
});
emit TreasuryConfigChanged(token, minReserve, reserveRatio, enabled);
}
/**
* @dev 检查并自动转移资金到金库
* @param token 代币地址
* @notice 任何人都可以调用,触发金库转移检查
*/
function autoTransferToTreasury(address token) public {
require(treasury != address(0), "treasury not set");
TreasuryConfig memory config = treasuryConfigs[token];
require(config.enabled, "treasury transfer not enabled for token");
uint256 currentBalance = IERC20(token).balanceOf(address(this));
// 计算按比例应该保留的金额
uint256 reserveByRatio = (currentBalance * config.reserveRatio) / 10000;
// 使用较大的保留金额(比例保留 vs 最小保留)
uint256 actualReserve = reserveByRatio > config.minReserve ? reserveByRatio : config.minReserve;
// 只有当比例保留金额大于等于最小保留金额时才转移
if (reserveByRatio >= config.minReserve && currentBalance > actualReserve) {
uint256 transferAmount = currentBalance - actualReserve;
require(IERC20(token).transfer(treasury, transferAmount), "treasury transfer failed");
treasuryTotal[token] += transferAmount;
emit TreasuryTransfer(token, transferAmount, actualReserve, treasuryTotal[token]);
}
}
/**
* @dev 计算代币的金库转移信息
* @param token 代币地址
* @return canTransfer 是否可以转移
* @return transferAmount 可转移金额
* @return willReserve 将保留的金额
* @return currentBalance 当前余额
*/
function calculateTreasuryTransfer(address token)
external
view
returns (
bool canTransfer,
uint256 transferAmount,
uint256 willReserve,
uint256 currentBalance
)
{
TreasuryConfig memory config = treasuryConfigs[token];
currentBalance = IERC20(token).balanceOf(address(this));
if (!config.enabled || treasury == address(0)) {
return (false, 0, currentBalance, currentBalance);
}
uint256 reserveByRatio = (currentBalance * config.reserveRatio) / 10000;
willReserve = reserveByRatio > config.minReserve ? reserveByRatio : config.minReserve;
canTransfer = reserveByRatio >= config.minReserve && currentBalance > willReserve;
transferAmount = canTransfer ? currentBalance - willReserve : 0;
}
/**
* @dev 内部函数:从数组中移除指定代币
* @param tokenArray 代币地址数组
* @param token 要移除的代币地址
*/
function _removeTokenFromArray(address[] storage tokenArray, address token) internal {
for (uint256 i = 0; i < tokenArray.length; i++) {
if (tokenArray[i] == token) {
tokenArray[i] = tokenArray[tokenArray.length - 1];
tokenArray.pop();
break;
}
}
}
/**
* @dev 执行转出操作
* @param token 要转出的代币合约地址
* @param amount 转出数量
* @param toChainID 目标链ID
* @param receiver 目标链上的接收者地址
* @notice 用户调用此函数将代币转移到其他链
* @notice 会收取配置的手续费,并检查转移限额
*/
function outTransfer(address token, uint256 amount, uint256 toChainID, address receiver)
external
notCurrentChain(toChainID)
{
// 获取转出配置
OutConfig storage config = outConfiguration[token][toChainID];
require(config.receiveToken != address(0), "not allow transfer");
require(config.enabled, "token transfer disabled");
require(amount > config.fee, "less than fee");
require(amount <= config.limit, "more than limit");
// 转移代币到合约
require(IERC20(token).transferFrom(msg.sender, address(this), amount), "transfer err");
// 如果配置为销毁模式,销毁代币
if (config.isBurn) {
IERC20Burnable(token).burn(amount);
}
// 获取当前链ID
uint256 fromChainID;
assembly {
fromChainID := chainid()
}
// 生成新的转出ID
_outID++;
uint256 outId = _outID;
// 记录转出信息
OutInfo storage info = outTransfers[outId];
info.outId = outId;
info.fromChainID = fromChainID;
info.sender = msg.sender;
info.token = token;
info.amount = amount;
info.fee = config.fee;
info.toChainID = toChainID;
info.receiver = receiver;
info.receiveToken = config.receiveToken;
info.receiveAmount = amount - config.fee;
// 生成签名用于验证
info.signature = keccak256(abi.encode(_outID, fromChainID, msg.sender, token, amount, toChainID, receiver, config.receiveToken));
// 更新转出总量
outTotal[token] = outTotal[token] + amount;
// 触发转出事件
emit TransferOut(outId, fromChainID, msg.sender, token, amount, info.fee, toChainID, receiver, config.receiveToken);
// 自动检查金库转移
if (treasury != address(0) && treasuryConfigs[token].enabled) {
autoTransferToTreasury(token);
}
}
/**
* @dev 提交转入操作参数结构体
* @param toChainID 目标链ID
* @param receiver 接收者地址
* @param token 目标链代币地址
* @param amount 转入金额
* @param outId 对应的转出操作ID
* @param fromChainID 源链ID
* @param sender 原始发送者地址
* @param sendToken 源链代币地址
* @param sendAmount 源链转出金额
* @param signature 转出操作签名
*/
struct submitParams {
uint256 toChainID;
address receiver;
address token;
uint256 amount;
uint256 outId;
uint256 fromChainID;
address sender;
address sendToken;
uint256 sendAmount;
bytes32 signature;
}
/**
* @dev 验证者提交转入操作
* @param params 转入操作参数
* @notice 验证者监听其他链的转出事件后调用此函数
* @notice 会验证签名并创建或更新转入记录
*/
function submitInTransfer(submitParams memory params)
external
onlyValidator
notCurrentChain(params.fromChainID)
currentChain(params.toChainID)
{
// 验证签名
bytes32 _signature = keccak256(abi.encode(params.outId, params.fromChainID, params.sender, params.sendToken, params.sendAmount, params.toChainID, params.receiver, params.token));
require(params.signature == _signature, "signature not equal");
// 检查是否已存在对应的转入记录
uint256 inId = getInId[params.fromChainID][params.outId];
if (inId == 0) {
// 创建新的转入记录
_inID++;
inId = _inID;
InInfo storage info = inTransfers[inId];
info.inId = inId;
info.token = params.token;
info.receiver = params.receiver;
info.amount = params.amount;
info.fromChainID = params.fromChainID;
info.outId = params.outId;
// 建立映射关系
getInId[params.fromChainID][params.outId] = inId;
// 触发转入事件
emit TransferIn(params.fromChainID, params.outId, inId, params.receiver, params.token, params.amount);
} else {
// 验证现有记录的一致性
require(inTransfers[inId].token == params.token, "token not equal");
require(inTransfers[inId].receiver == params.receiver, "receiver not equal");
require(inTransfers[inId].amount == params.amount, "amount not equal");
}
// 自动确认该转入操作
_confirm(inId);
}
/**
* @dev 验证者确认转入操作
* @param inId 转入操作ID
* @notice 验证者可以单独确认已提交的转入操作
*/
function confirmInTransfer(uint256 inId) public onlyValidator {
_confirm(inId);
}
/**
* @dev 验证者拒绝转入操作
* @param inId 转入操作ID
* @notice 验证者发现问题时可以拒绝转入操作
*/
function rejectInTransfer(uint256 inId) public onlyValidator {
bool rejected = inTransfers[inId].rejections[msg.sender];
if (!rejected) {
inTransfers[inId].rejections[msg.sender] = true;
inTransfers[inId].rejectCounter++;
emit TransferInRejection(inId, msg.sender);
}
}
/**
* @dev 内部确认函数
* @param inId 转入操作ID
* @notice 处理验证者的确认操作,达到条件时自动执行转账
*/
function _confirm(uint256 inId) internal {
require(inTransfers[inId].rejectCounter == 0, "already rejected");
bool confirmed = inTransfers[inId].confirmations[msg.sender];
if (!confirmed) {
// 记录确认
inTransfers[inId].confirmations[msg.sender] = true;
inTransfers[inId].confirmCounter++;
emit TransferInConfirmation(inId, msg.sender);
// 检查是否达到执行条件
if (isChecked(inId)) {
_finalizeTransfer(inId);
}
}
}
/**
* @dev 内部转账执行函数
* @param inId 转入操作ID
* @notice 在获得足够确认后执行实际的代币转账
*/
function _finalizeTransfer(uint256 inId) internal {
address token = inTransfers[inId].token;
address receiver = inTransfers[inId].receiver;
uint256 amount = inTransfers[inId].amount;
// 安全检查
require(inTransfers[inId].executed == false, "already executed");
require(inTransfers[inId].rejectCounter == 0, "already rejected");
require(IERC20(token).balanceOf(address(this)) >= amount, "not have enough token");
// 标记为已执行
inTransfers[inId].executed = true;
// 执行代币转账
require(IERC20(token).transfer(receiver, amount), "transfer err");
// 更新转入总量
inTotal[token] = inTotal[token] + amount;
// 触发执行事件
emit TransferInExecution(inId, receiver, token, amount);
}
/**
* @dev 管理员强制执行转账
* @param inId 转入操作ID
* @param receiver 接收者地址
* @param token 代币地址
* @param amount 转账金额
* @notice 只有合约所有者可以调用,用于紧急情况处理
* @notice 此函数会绕过验证者确认机制
*/
function retryTransfer(uint256 inId, address receiver, address token, uint256 amount)
external
onlyOwner
{
require(inTransfers[inId].executed == false, "already executed");
require(IERC20(token).balanceOf(address(this)) >= amount, "not have enough token");
// 更新转入记录
inTransfers[inId].executed = true;
inTransfers[inId].receiver = receiver;
inTransfers[inId].token = token;
inTransfers[inId].amount = amount;
// 执行转账
IERC20(token).transfer(receiver, amount);
inTotal[token] = inTotal[token] + amount;
emit TransferInExecution(inId, receiver, token, amount);
}
/**
* @dev 提取合约中的代币
* @param token 代币合约地址
* @param amount 提取数量
* @param to 接收地址
* @notice 只有合约所有者可以调用,用于紧急情况或费用提取
*/
function emergencyWithdraw(address token, uint256 amount, address to) external onlyOwner {
require(IERC20(token).balanceOf(address(this)) >= amount, "not enough");
IERC20(token).transfer(to, amount);
}
/**
* @dev 检查转入操作是否已通过验证
* @param inId 转入操作ID
* @return 是否已通过验证(无拒绝且确认数足够)
*/
function isChecked(uint256 inId) public view returns (bool) {
return (inTransfers[inId].rejectCounter == 0 && inTransfers[inId].confirmCounter >= validRequired);
}
/**
* @dev 检查验证者是否已确认指定转入操作
* @param inId 转入操作ID
* @param validator 验证者地址
* @return 是否已确认
*/
function isConfirmed(uint256 inId, address validator) public view returns (bool) {
return inTransfers[inId].confirmations[validator];
}
/**
* @dev 检查验证者是否已拒绝指定转入操作
* @param inId 转入操作ID
* @param validator 验证者地址
* @return 是否已拒绝
*/
function isRejected(uint256 inId, address validator) public view returns (bool) {
return inTransfers[inId].rejections[validator];
}
/**
* @dev 获取指定目标链支持的代币列表
* @param toChainID 目标链ID
* @return 支持转出的代币地址数组
*/
function getSupportedTokensOut(uint256 toChainID) external view returns (address[] memory) {
return supportedTokensOut[toChainID];
}
/**
* @dev 检查代币是否支持转出到指定链
* @param token 代币地址
* @param toChainID 目标链ID
* @return 是否支持
*/
function isTokenTransferEnabled(address token, uint256 toChainID) external view returns (bool) {
return outConfiguration[token][toChainID].enabled &&
outConfiguration[token][toChainID].receiveToken != address(0);
}
}
\ No newline at end of file
pragma solidity >=0.5.0;
pragma experimental ABIEncoderV2;
/// @title Multicall2 - Aggregate results from multiple read-only function calls
/// @author Michael Elliot <mike@makerdao.com>
/// @author Joshua Levine <joshua@makerdao.com>
/// @author Nick Johnson <arachnid@notdot.net>
contract Multicall2 {
struct Call {
address target;
bytes callData;
}
struct Result {
bool success;
bytes returnData;
}
function aggregate(Call[] memory calls) public returns (uint256 blockNumber, bytes[] memory returnData) {
blockNumber = block.number;
returnData = new bytes[](calls.length);
for(uint256 i = 0; i < calls.length; i++) {
(bool success, bytes memory ret) = calls[i].target.call(calls[i].callData);
require(success, "Multicall aggregate: call failed");
returnData[i] = ret;
}
}
function blockAndAggregate(Call[] memory calls) public returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData) {
(blockNumber, blockHash, returnData) = tryBlockAndAggregate(true, calls);
}
function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) {
blockHash = blockhash(blockNumber);
}
function getBlockNumber() public view returns (uint256 blockNumber) {
blockNumber = block.number;
}
function getCurrentBlockCoinbase() public view returns (address coinbase) {
coinbase = block.coinbase;
}
function getCurrentBlockDifficulty() public view returns (uint256 difficulty) {
difficulty = block.difficulty;
}
function getCurrentBlockGasLimit() public view returns (uint256 gaslimit) {
gaslimit = block.gaslimit;
}
function getCurrentBlockTimestamp() public view returns (uint256 timestamp) {
timestamp = block.timestamp;
}
function getEthBalance(address addr) public view returns (uint256 balance) {
balance = addr.balance;
}
function getLastBlockHash() public view returns (bytes32 blockHash) {
blockHash = blockhash(block.number - 1);
}
function tryAggregate(bool requireSuccess, Call[] memory calls) public returns (Result[] memory returnData) {
returnData = new Result[](calls.length);
for(uint256 i = 0; i < calls.length; i++) {
(bool success, bytes memory ret) = calls[i].target.call(calls[i].callData);
if (requireSuccess) {
require(success, "Multicall2 aggregate: call failed");
}
returnData[i] = Result(success, ret);
}
}
function tryBlockAndAggregate(bool requireSuccess, Call[] memory calls) public returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData) {
blockNumber = block.number;
blockHash = blockhash(block.number);
returnData = tryAggregate(requireSuccess, calls);
}
}
\ No newline at end of file
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
pragma solidity ^0.8.10;
contract RevertContract {
event log(string message);
modifier minimumEther(uint256 _amount) {
require(_amount >= 1*10**18, "minimum 1 ether");
_;
......@@ -12,12 +13,14 @@ contract RevertContract {
}
function sendViaSend(address payable _to, uint256 _amount) public payable minimumEther(_amount) returns (bool) {
emit log("sendViaSend");
bool sent = _to.send(_amount);
require(sent, "Send failed");
return sent;
}
function sendViaCall(address payable _to, uint256 _amount) public payable minimumEther(_amount) returns (bool) {
emit log("sendViaCall");
(bool success, ) = _to.call{value: _amount}("");
require(success, "Call failed");
return success;
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract TestToken is ERC20 {
constructor(string memory name, string memory symbol) ERC20(name, symbol) {
_mint(msg.sender, 1000000000 * 10 ** decimals());
}
function burn(uint256 amount) public {
_burn(msg.sender, amount);
}
}
\ No newline at end of file
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title Bridge
* @dev 跨链桥合约,支持代币在不同区块链之间转移
* @notice 使用多签验证者机制确保跨链转移的安全性
*/
contract TestAbi is Ownable {
/// @dev 执行转入操作所需的最少验证者确认数量
uint256 public validRequired;
/// @dev 记录地址是否为验证者
mapping(address => bool) public isValidator;
/**
* @dev 转出配置结构体
* @param receiveToken 在目标链上接收的代币合约地址
* @param fee 转移手续费
* @param limit 单次转移的最大金额限制
* @param isBurn 是否销毁源链上的代币(用于总供应量固定的代币)
* @param enabled 是否启用该转移路径
*/
struct OutConfig {
address receiveToken;
uint256 fee;
uint256 limit;
bool isBurn;
bool enabled;
}
/// @dev 转出配置映射:源链代币地址 => 目标链ID => 转出配置
mapping(address => mapping(uint256 => OutConfig)) public outConfiguration;
/// @dev 获取支持转出的代币列表:目标链ID => 代币地址数组
mapping(uint256 => address[]) public supportedTokensOut;
/// @dev 检查代币是否支持转出:目标链ID => 代币地址 => 是否支持
mapping(uint256 => mapping(address => bool)) public isTokenSupportedOut;
/// @dev 转出记录映射:转出ID => 转出信息
mapping(uint256 => OutInfo) public outTransfers;
/// @dev 转入记录映射:转入ID => 转入信息
mapping(uint256 => InInfo) public inTransfers;
/// @dev 获取转入ID映射:源链ID => 转出ID => 转入ID
mapping(uint256 => mapping(uint256 => uint256)) public getInId;
/// @dev 转出操作的自增ID计数器
uint256 public _outID;
/// @dev 转入操作的自增ID计数器
uint256 public _inID;
/// @dev 每种代币的累计转出总量
mapping(address => uint256) public outTotal;
/// @dev 每种代币的累计转入总量
mapping(address => uint256) public inTotal;
/// @dev 金库地址
address public treasury;
/**
* @dev 代币金库配置结构体
* @param minReserve 最小保留金额(合约必须保留的最少数量)
* @param reserveRatio 保留比例(基数10000,如5000表示50%)
* @param enabled 是否启用金库转移功能
*/
struct TreasuryConfig {
uint256 minReserve; // 最小保留金额
uint256 reserveRatio; // 保留比例(基数10000)
bool enabled; // 是否启用
}
/// @dev 每个代币的金库配置:代币地址 => 金库配置
mapping(address => TreasuryConfig) public treasuryConfigs;
/// @dev 每个代币转移到金库的累计总量
mapping(address => uint256) public treasuryTotal;
/**
* @dev 转出操作信息结构体
* @param outId 转出操作的唯一ID
* @param fromChainID 源链ID
* @param sender 发送者地址
* @param token 转出的代币合约地址
* @param amount 转出金额
* @param fee 手续费
* @param toChainID 目标链ID
* @param receiver 接收者地址
* @param receiveToken 在目标链上接收的代币合约地址
* @param receiveAmount 实际接收金额(扣除手续费后)
* @param signature 操作签名,用于验证
*/
struct OutInfo {
uint256 outId;
uint256 fromChainID;
address sender;
address token;
uint256 amount;
uint256 fee;
uint256 toChainID;
address receiver;
address receiveToken;
uint256 receiveAmount;
bytes32 signature;
}
/**
* @dev 转入操作信息结构体
* @param inId 转入操作的唯一ID
* @param token 转入的代币合约地址
* @param receiver 接收者地址
* @param fee 手续费
* @param amount 转入金额
* @param fromChainID 源链ID
* @param outId 对应的转出操作ID
* @param executed 是否已执行
* @param confirmCounter 确认数量计数器
* @param confirmations 验证者确认记录
* @param rejectCounter 拒绝数量计数器
* @param rejections 验证者拒绝记录
*/
struct InInfo {
uint256 inId;
address token;
address receiver;
uint256 fee;
uint256 amount;
uint256 fromChainID;
uint256 outId;
bool executed;
uint256 confirmCounter;
mapping(address => bool) confirmations;
uint256 rejectCounter;
mapping(address => bool) rejections;
}
// ============ 事件定义 ============
/**
* @dev 转出操作事件
* @param outId 转出ID
* @param fromChainID 源链ID
* @param sender 发送者
* @param token 代币地址
* @param amount 转出金额
* @param fee 手续费
* @param toChainID 目标链ID
* @param receiver 接收者
* @param receiveToken 接收代币地址
*/
event TransferOut(
uint256 outId,
uint256 fromChainID,
address sender,
address token,
uint256 amount,
uint256 fee,
uint256 toChainID,
address receiver,
address receiveToken
);
/**
* @dev 转入操作事件
* @param fromChainID 源链ID
* @param outId 对应的转出ID
* @param inId 转入ID
* @param receiver 接收者
* @param token 代币地址
* @param amount 转入金额
*/
event TransferIn(
uint256 fromChainID,
uint256 outId,
uint256 inId,
address receiver,
address token,
uint256 amount
);
/**
* @dev 转入确认事件
* @param inId 转入ID
* @param validator 确认的验证者
*/
event TransferInConfirmation(
uint256 inId,
address validator
);
/**
* @dev 转入拒绝事件
* @param inId 转入ID
* @param validator 拒绝的验证者
*/
event TransferInRejection(
uint256 inId,
address validator
);
/**
* @dev 转入执行事件
* @param inId 转入ID
* @param receiver 接收者
* @param token 代币地址
* @param amount 转入金额
*/
event TransferInExecution(
uint256 inId,
address receiver,
address token,
uint256 amount
);
/// @dev 代币配置变更事件
event TokenConfigChanged(address indexed token, uint256 indexed toChainID, bool enabled);
/// @dev 金库地址变更事件
event TreasuryChanged(address indexed oldTreasury, address indexed newTreasury);
/// @dev 金库配置变更事件
event TreasuryConfigChanged(address indexed token, uint256 minReserve, uint256 reserveRatio, bool enabled);
/// @dev 资金转移到金库事件
event TreasuryTransfer(address indexed token, uint256 amount, uint256 contractBalance, uint256 treasuryBalance);
/// @dev 验证者添加事件
event ValidatorAddition(address validator);
/// @dev 验证者移除事件
event ValidatorRemoval(address validator);
/// @dev 所需确认数变更事件
event RequirementChange(uint prior, uint256 present);
// ============ 构造函数 ============
/**
* @notice 初始化合约,设置初始所需确认数为1
* @param initialOwner 合约的初始所有者
*/
constructor(address initialOwner) Ownable(initialOwner) {
validRequired = 1;
treasury = address(0); // 初始化金库地址为0,需要后续设置
}
// ============ 验证者管理函数 ============
/**
* @dev 添加验证者
* @param validator 要添加的验证者地址
* @notice 只有合约所有者可以调用
*/
function addValidator(address validator) external onlyOwner {
isValidator[validator] = true;
emit ValidatorAddition(validator);
}
/**
* @dev 移除验证者
* @param validator 要移除的验证者地址
* @notice 只有合约所有者可以调用
*/
function removeValidator(address validator) external onlyOwner {
isValidator[validator] = false;
emit ValidatorRemoval(validator);
}
/**
* @dev 替换验证者
* @param validator 要被替换的验证者地址
* @param newValidator 新验证者地址
* @notice 只有合约所有者可以调用
* @notice 函数名存在拼写错误,应为replaceValidator
*/
function replaceValidator(address validator, address newValidator) external onlyOwner {
isValidator[validator] = false;
isValidator[newValidator] = true;
emit ValidatorAddition(newValidator);
emit ValidatorRemoval(validator);
}
// ============ 修饰符 ============
/**
* @dev 验证者权限修饰符
* @notice 确保调用者是验证者
*/
modifier onlyValidator {
require(isValidator[msg.sender], "not validator");
_;
}
/**
* @dev 非当前链修饰符
* @param chainID 要检查的链ID
* @notice 确保指定链ID不是当前链
*/
modifier notCurrentChain(uint256 chainID) {
uint256 id;
assembly {
id := chainid()
}
require(chainID != id, "can't current chain");
_;
}
/**
* @dev 当前链修饰符
* @param chainID 要检查的链ID
* @notice 确保指定链ID是当前链
*/
modifier currentChain(uint256 chainID) {
uint256 id;
assembly {
id := chainid()
}
require(chainID == id, "must current chain");
_;
}
// ============ 配置管理函数 ============
/**
* @dev 修改所需验证者确认数量
* @param required 新的所需确认数量
* @notice 只有合约所有者可以调用
*/
function changeValidRequired(uint256 required) external onlyOwner {
uint256 prior = validRequired;
validRequired = required;
emit RequirementChange(prior, required);
}
/**
* @dev 修改转出配置
* @param token 代币合约地址
* @param toChainID 目标链ID
* @param config 新的转出配置
* @notice 只有合约所有者可以调用
* @notice 目标链不能是当前链
*/
function changeOutConfig(address token, uint256 toChainID, OutConfig memory config) external onlyOwner notCurrentChain(toChainID) {
bool wasEnabled = outConfiguration[token][toChainID].enabled;
outConfiguration[token][toChainID] = config;
// 如果是新启用的代币,添加到支持列表
if (config.enabled && !wasEnabled) {
if (!isTokenSupportedOut[toChainID][token]) {
supportedTokensOut[toChainID].push(token);
isTokenSupportedOut[toChainID][token] = true;
}
}
// 如果是禁用代币,从支持列表移除
else if (!config.enabled && wasEnabled) {
_removeTokenFromArray(supportedTokensOut[toChainID], token);
isTokenSupportedOut[toChainID][token] = false;
}
emit TokenConfigChanged(token, toChainID, config.enabled);
}
/**
* @dev 设置金库地址
* @param newTreasury 新的金库地址
* @notice 只有合约所有者可以调用
*/
function setTreasury(address newTreasury) external onlyOwner {
require(newTreasury != address(0), "treasury cannot be zero address");
address oldTreasury = treasury;
treasury = newTreasury;
emit TreasuryChanged(oldTreasury, newTreasury);
}
/**
* @dev 设置代币的金库配置
* @param token 代币地址
* @param minReserve 最小保留金额
* @param reserveRatio 保留比例(基数10000,如5000表示50%)
* @param enabled 是否启用金库功能
* @notice 只有合约所有者可以调用
*/
function setTreasuryConfig(
address token,
uint256 minReserve,
uint256 reserveRatio,
bool enabled
) external onlyOwner {
require(reserveRatio <= 10000, "reserve ratio cannot exceed 100%");
require(minReserve > 0 || !enabled, "min reserve must be positive when enabled");
treasuryConfigs[token] = TreasuryConfig({
minReserve: minReserve,
reserveRatio: reserveRatio,
enabled: enabled
});
emit TreasuryConfigChanged(token, minReserve, reserveRatio, enabled);
}
/**
* @dev 检查并自动转移资金到金库
* @param token 代币地址
* @notice 任何人都可以调用,触发金库转移检查
*/
function autoTransferToTreasury(address token) public {
require(treasury != address(0), "treasury not set");
TreasuryConfig memory config = treasuryConfigs[token];
require(config.enabled, "treasury transfer not enabled for token");
uint256 currentBalance = IERC20(token).balanceOf(address(this));
// 计算按比例应该保留的金额
uint256 reserveByRatio = (currentBalance * config.reserveRatio) / 10000;
// 使用较大的保留金额(比例保留 vs 最小保留)
uint256 actualReserve = reserveByRatio > config.minReserve ? reserveByRatio : config.minReserve;
// 只有当比例保留金额大于等于最小保留金额时才转移
if (reserveByRatio >= config.minReserve && currentBalance > actualReserve) {
uint256 transferAmount = currentBalance - actualReserve;
require(IERC20(token).transfer(treasury, transferAmount), "treasury transfer failed");
treasuryTotal[token] += transferAmount;
emit TreasuryTransfer(token, transferAmount, actualReserve, treasuryTotal[token]);
}
}
/**
* @dev 计算代币的金库转移信息
* @param token 代币地址
* @return canTransfer 是否可以转移
* @return transferAmount 可转移金额
* @return willReserve 将保留的金额
* @return currentBalance 当前余额
*/
function calculateTreasuryTransfer(address token)
external
view
returns (
bool canTransfer,
uint256 transferAmount,
uint256 willReserve,
uint256 currentBalance
)
{
TreasuryConfig memory config = treasuryConfigs[token];
currentBalance = IERC20(token).balanceOf(address(this));
if (!config.enabled || treasury == address(0)) {
return (false, 0, currentBalance, currentBalance);
}
uint256 reserveByRatio = (currentBalance * config.reserveRatio) / 10000;
willReserve = reserveByRatio > config.minReserve ? reserveByRatio : config.minReserve;
canTransfer = reserveByRatio >= config.minReserve && currentBalance > willReserve;
transferAmount = canTransfer ? currentBalance - willReserve : 0;
}
/**
* @dev 内部函数:从数组中移除指定代币
* @param tokenArray 代币地址数组
* @param token 要移除的代币地址
*/
function _removeTokenFromArray(address[] storage tokenArray, address token) internal {
for (uint256 i = 0; i < tokenArray.length; i++) {
if (tokenArray[i] == token) {
tokenArray[i] = tokenArray[tokenArray.length - 1];
tokenArray.pop();
break;
}
}
}
/**
* @dev 执行转出操作
* @param token 要转出的代币合约地址
* @param amount 转出数量
* @param toChainID 目标链ID
* @param receiver 目标链上的接收者地址
* @notice 用户调用此函数将代币转移到其他链
* @notice 会收取配置的手续费,并检查转移限额
*/
function outTransfer(address token, uint256 amount, uint256 toChainID, address receiver)
external
notCurrentChain(toChainID)
{
// 获取转出配置
OutConfig storage config = outConfiguration[token][toChainID];
require(config.receiveToken != address(0), "not allow transfer");
require(config.enabled, "token transfer disabled");
require(amount > config.fee, "less than fee");
require(amount <= config.limit, "more than limit");
// 转移代币到合约
require(IERC20(token).transferFrom(msg.sender, address(this), amount), "transfer err");
// 如果配置为销毁模式,销毁代币
if (config.isBurn) {
}
// 获取当前链ID
uint256 fromChainID;
assembly {
fromChainID := chainid()
}
// 生成新的转出ID
_outID++;
uint256 outId = _outID;
// 记录转出信息
OutInfo storage info = outTransfers[outId];
info.outId = outId;
info.fromChainID = fromChainID;
info.sender = msg.sender;
info.token = token;
info.amount = amount;
info.fee = config.fee;
info.toChainID = toChainID;
info.receiver = receiver;
info.receiveToken = config.receiveToken;
info.receiveAmount = amount - config.fee;
// 生成签名用于验证
info.signature = keccak256(abi.encode(_outID, fromChainID, msg.sender, token, amount, toChainID, receiver, config.receiveToken));
// 更新转出总量
outTotal[token] = outTotal[token] + amount;
// 触发转出事件
emit TransferOut(outId, fromChainID, msg.sender, token, amount, info.fee, toChainID, receiver, config.receiveToken);
// 自动检查金库转移
if (treasury != address(0) && treasuryConfigs[token].enabled) {
autoTransferToTreasury(token);
}
}
/**
* @dev 提交转入操作参数结构体
* @param toChainID 目标链ID
* @param receiver 接收者地址
* @param token 目标链代币地址
* @param amount 转入金额
* @param outId 对应的转出操作ID
* @param fromChainID 源链ID
* @param sender 原始发送者地址
* @param sendToken 源链代币地址
* @param sendAmount 源链转出金额
* @param signature 转出操作签名
*/
struct submitParams {
uint256 toChainID;
address receiver;
address token;
uint256 amount;
uint256 outId;
uint256 fromChainID;
address sender;
address sendToken;
uint256 sendAmount;
bytes32 signature;
}
function getSubmitData(submitParams memory params)
external
view
returns (bytes32) {
return keccak256(abi.encode(params.outId, params.fromChainID, params.sender, params.sendToken, params.sendAmount, params.toChainID, params.receiver, params.token));
}
/**
* @dev 验证者提交转入操作
* @param params 转入操作参数
* @notice 验证者监听其他链的转出事件后调用此函数
* @notice 会验证签名并创建或更新转入记录
*/
function submitInTransfer(submitParams memory params)
external
onlyValidator
notCurrentChain(params.fromChainID)
currentChain(params.toChainID)
{
// 验证签名
bytes32 _signature = keccak256(abi.encode(params.outId, params.fromChainID, params.sender, params.sendToken, params.sendAmount, params.toChainID, params.receiver, params.token));
require(params.signature == _signature, "signature not equal");
// 检查是否已存在对应的转入记录
uint256 inId = getInId[params.fromChainID][params.outId];
if (inId == 0) {
// 创建新的转入记录
_inID++;
inId = _inID;
InInfo storage info = inTransfers[inId];
info.inId = inId;
info.token = params.token;
info.receiver = params.receiver;
info.amount = params.amount;
info.fromChainID = params.fromChainID;
info.outId = params.outId;
// 建立映射关系
getInId[params.fromChainID][params.outId] = inId;
// 触发转入事件
emit TransferIn(params.fromChainID, params.outId, inId, params.receiver, params.token, params.amount);
} else {
// 验证现有记录的一致性
require(inTransfers[inId].token == params.token, "token not equal");
require(inTransfers[inId].receiver == params.receiver, "receiver not equal");
require(inTransfers[inId].amount == params.amount, "amount not equal");
}
// 自动确认该转入操作
_confirm(inId);
}
/**
* @dev 验证者确认转入操作
* @param inId 转入操作ID
* @notice 验证者可以单独确认已提交的转入操作
*/
function confirmInTransfer(uint256 inId) public onlyValidator {
_confirm(inId);
}
/**
* @dev 验证者拒绝转入操作
* @param inId 转入操作ID
* @notice 验证者发现问题时可以拒绝转入操作
*/
function rejectInTransfer(uint256 inId) public onlyValidator {
bool rejected = inTransfers[inId].rejections[msg.sender];
if (!rejected) {
inTransfers[inId].rejections[msg.sender] = true;
inTransfers[inId].rejectCounter++;
emit TransferInRejection(inId, msg.sender);
}
}
/**
* @dev 内部确认函数
* @param inId 转入操作ID
* @notice 处理验证者的确认操作,达到条件时自动执行转账
*/
function _confirm(uint256 inId) internal {
require(inTransfers[inId].rejectCounter == 0, "already rejected");
bool confirmed = inTransfers[inId].confirmations[msg.sender];
if (!confirmed) {
// 记录确认
inTransfers[inId].confirmations[msg.sender] = true;
inTransfers[inId].confirmCounter++;
emit TransferInConfirmation(inId, msg.sender);
// 检查是否达到执行条件
if (isChecked(inId)) {
_finalizeTransfer(inId);
}
}
}
/**
* @dev 内部转账执行函数
* @param inId 转入操作ID
* @notice 在获得足够确认后执行实际的代币转账
*/
function _finalizeTransfer(uint256 inId) internal {
address token = inTransfers[inId].token;
address receiver = inTransfers[inId].receiver;
uint256 amount = inTransfers[inId].amount;
// 安全检查
require(inTransfers[inId].executed == false, "already executed");
require(inTransfers[inId].rejectCounter == 0, "already rejected");
require(IERC20(token).balanceOf(address(this)) >= amount, "not have enough token");
// 标记为已执行
inTransfers[inId].executed = true;
// 执行代币转账
require(IERC20(token).transfer(receiver, amount), "transfer err");
// 更新转入总量
inTotal[token] = inTotal[token] + amount;
// 触发执行事件
emit TransferInExecution(inId, receiver, token, amount);
}
/**
* @dev 管理员强制执行转账
* @param inId 转入操作ID
* @param receiver 接收者地址
* @param token 代币地址
* @param amount 转账金额
* @notice 只有合约所有者可以调用,用于紧急情况处理
* @notice 此函数会绕过验证者确认机制
*/
function retryTransfer(uint256 inId, address receiver, address token, uint256 amount)
external
onlyOwner
{
require(inTransfers[inId].executed == false, "already executed");
require(IERC20(token).balanceOf(address(this)) >= amount, "not have enough token");
// 更新转入记录
inTransfers[inId].executed = true;
inTransfers[inId].receiver = receiver;
inTransfers[inId].token = token;
inTransfers[inId].amount = amount;
// 执行转账
IERC20(token).transfer(receiver, amount);
inTotal[token] = inTotal[token] + amount;
emit TransferInExecution(inId, receiver, token, amount);
}
/**
* @dev 提取合约中的代币
* @param token 代币合约地址
* @param amount 提取数量
* @param to 接收地址
* @notice 只有合约所有者可以调用,用于紧急情况或费用提取
*/
function emergencyWithdraw(address token, uint256 amount, address to) external onlyOwner {
require(IERC20(token).balanceOf(address(this)) >= amount, "not enough");
IERC20(token).transfer(to, amount);
}
/**
* @dev 检查转入操作是否已通过验证
* @param inId 转入操作ID
* @return 是否已通过验证(无拒绝且确认数足够)
*/
function isChecked(uint256 inId) public view returns (bool) {
return (inTransfers[inId].rejectCounter == 0 && inTransfers[inId].confirmCounter >= validRequired);
}
/**
* @dev 检查验证者是否已确认指定转入操作
* @param inId 转入操作ID
* @param validator 验证者地址
* @return 是否已确认
*/
function isConfirmed(uint256 inId, address validator) public view returns (bool) {
return inTransfers[inId].confirmations[validator];
}
/**
* @dev 检查验证者是否已拒绝指定转入操作
* @param inId 转入操作ID
* @param validator 验证者地址
* @return 是否已拒绝
*/
function isRejected(uint256 inId, address validator) public view returns (bool) {
return inTransfers[inId].rejections[validator];
}
/**
* @dev 获取指定目标链支持的代币列表
* @param toChainID 目标链ID
* @return 支持转出的代币地址数组
*/
function getSupportedTokensOut(uint256 toChainID) external view returns (address[] memory) {
return supportedTokensOut[toChainID];
}
/**
* @dev 检查代币是否支持转出到指定链
* @param token 代币地址
* @param toChainID 目标链ID
* @return 是否支持
*/
function isTokenTransferEnabled(address token, uint256 toChainID) external view returns (bool) {
return outConfiguration[token][toChainID].enabled &&
outConfiguration[token][toChainID].receiveToken != address(0);
}
}
\ No newline at end of file
......@@ -33,6 +33,7 @@ const accounts = { mnemonic }
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: {
evmVersion: "istanbul",
compilers: [
{
version: "0.8.20",
......@@ -43,11 +44,26 @@ module.exports = {
},
},
},
{
version: "0.8.10",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
],
},
sourcify: {
enabled: false
},
etherscan: {
apiKey: {
'achain': 'empty',
'hole': 'empty',
'movadev': 'empty',
'mova': 'empty',
'bsc':'AVZIFYCHUFHPG9FDKNMHEWJ1VAW1H5U66T'
},
customChains: [
......@@ -58,6 +74,30 @@ module.exports = {
apiURL: "https://scan.cmp20.bitheart.org/api",
browserURL: "https://scan.cmp20.bitheart.org"
}
},
{
network: "hole",
chainId: 6174,
urls: {
apiURL: "https://holescan.bitheart.org/api",
browserURL: "https://holescan.bitheart.org"
}
},
{
network: "movadev",
chainId: 8891,
urls: {
apiURL: "https://scan.mova.bitheart.org/api",
browserURL: "https://scan.mova.bitheart.org"
}
},
{
network: "mova",
chainId: 10323,
urls: {
apiURL: "https://scan.mars.movachain.com/api",
browserURL: "https://scan.mars.movachain.com"
}
}
]
},
......@@ -65,13 +105,19 @@ module.exports = {
hardhat: {
accounts: [{ privateKey, balance: "10000000000000000000000" }], // Example balance
gas: 10000000,
gasPrice: 10000000000,
gasPrice: 10000000000
},
hole: {
url: "https://rpc.hole.bitheart.org",
accounts: [privateKey]
},
achain: {
// url: "https://achain.bitheart.org",
// url: "http://192.168.1.39:28545",
url: "http://15.206.56.79:28545",
accounts: [privateKey],
movadev: {
url: "https://rpc.mova.bitheart.org",
accounts: [privateKey]
},
mova: {
url: "https://mars.rpc.movachain.com",
accounts: [privateKey]
}
},
};
const { ContractFactory } = require("ethers");
const hre = require("hardhat");
let initialValidators = process.env.BRIDGE_VALIDATORS
async function addValidators(contract, validators) {
let validatorList = validators.split(',')
for (let i = 0; i < validatorList.length; i++) {
var tx = await contract.addValidator(validatorList[i])
await tx.wait();
console.log("Added validator:", validatorList[i]);
}
}
// Define the script
async function deployAtSourceChain() {
// Deploy the contract
const factory = await hre.ethers.getContractFactory(
"Bridge"
);
const contract = await factory.deploy();
await contract.waitForDeployment();
const depAddr = await contract.getAddress();
console.log("Contract deployed for source at:", depAddr);
await initForSourceChain(contract);
}
async function initForSourceChain(bridge) {
// add validator
await addValidators(bridge, initialValidators);
// set
}
async function deployAtMovaChain() {
// Deploy the contract
const factory = await hre.ethers.getContractFactory(
"Bridge"
);
const contract = await factory.deploy();
await contract.waitForDeployment();
const depAddr = await contract.getAddress();
console.log("Contract deployed for mova at:", depAddr);
await initForMovaChain(contract);
}
async function initForMovaChain(bridge) {
await addValidators(bridge, initialValidators);
}
// Define the script
async function main() {
await deployAtSourceChain();
await deployAtMovaChain();
}
// Run the script
main().catch((error) => {
console.error("Error:", error);
});
\ No newline at end of file
const { ContractFactory } = require("ethers");
const hre = require("hardhat");
var addrlist = [
"0xaccf7aacd00bb765120f10685e58d916d3ec3057",
"0x4631638617F6CF476BeC1AA9A892D308AaE2062A",
"0x43Fa8B58AaBeF30bE4221C7c435Fa38d34f0ccb1",
"0x193b7fBA150679AC96c675964180dA078B73fE3c",
"0x5b89a423416B00769ecB9F148411CD686d165A43",
"0xDF2cf3eE3E4E36490F5f942372fF4c975040bBd0",
"0xdD9720aBe15CD07cc2BF9213C1f08CFf1AE0b37D",
"0xD7FB4128F5451f88A5e525ef5303d714633435A7",
"0xf5c5C8cC784C8183316f4d1f0E50c46DF45920c8",
"0x9923f67774A4250bf2B2eA34Da9cBbc6aBd98caD",
"0xe40701772cCBA7a515CE557c3eaeB4B29B92C71e"
]
async function test() {
for (let i = 0; i < addrlist.length; i++) {
const balance = await hre.ethers.provider.getBalance(addrlist[i]);
console.log("Address:", addrlist[i], " Balance:", hre.ethers.formatEther(balance), " ETH");
}
}
// Define the script
async function main() {
await test()
}
// Run the script
main().catch((error) => {
console.error("Error:", error);
});
\ No newline at end of file
const { ContractFactory, ethers} = require("ethers");
const hre = require("hardhat");
// Define the script
async function main() {
const factoryAddress = '0xedd8Da22D948f3F594FdC37840d961461814EeA3'
const bytecode = await hre.ethers.provider.getCode(factoryAddress)
const codeHash = hre.ethers.keccak256(bytecode);
console.log('Factory Code Hash:', codeHash);
}
// Run the script
main().catch((error) => {
console.error("Error:", error);
});
\ No newline at end of file
......@@ -9,18 +9,28 @@ async function forceRevert() {
await contract.waitForDeployment();
const contractAddress = await contract.getAddress();
console.log("Contract deployed at:", contractAddress);
return
const signer = new hre.ethers.Wallet(process.env.DEPLOY_PRIVATE_KEY, hre.ethers.provider);
const transferAmount = hre.ethers.parseEther("0.0005"); // 小于 1 ETH
var nonce = await hre.ethers.provider.getTransactionCount(signer.address)
const chainid = await hre.ethers.provider.getNetwork().then(network => network.chainId);
// Encode the function call
const abi = ["function sendViaTransfer(address payable _to, uint256 _amount)"];
const iface = new hre.ethers.Interface(abi);
const data = iface.encodeFunctionData("sendViaTransfer", ['0xAD4143Df8Fe9E31B1C4318e8dE555872085f481d', transferAmount]);
// 签名交易
const signedTx = await signer.signTransaction({
type: 0,
chainId: 100,
chainId: chainid,
nonce: nonce,
to: contractAddress,
data: '0xd27f8e66000000000000000000000000a686b69111eadc74d45eeb3be4ae487fd61497e60000000000000000000000000000000000000000000000000001c6bf52634000',
data: data,
value: transferAmount,
gasLimit: 1000000, // 设置 gas 限制
gasPrice: hre.ethers.parseUnits("1", "gwei"), // 设置 gas 价格
......
const { ContractFactory } = require("ethers");
const hre = require("hardhat");
// Define the script
async function main() {
// Deploy the contract
const factory = await hre.ethers.getContractFactory(
"HandoutContract"
);
const contract = await factory.deploy();
await contract.waitForDeployment();
const depAddr = await contract.getAddress();
console.log("Contract deployed at:", depAddr);
}
// Run the script
main().catch((error) => {
console.error("Error:", error);
});
\ No newline at end of file
......@@ -10,13 +10,14 @@ async function test() {
nonce = nonce + 1
const signedTx = await signer.signTransaction({
type: 0,
chainId: 100,
chainId: 10323,
nonce: nonce,
to: "0x88395111AB1586a4030dAC62a183542762929bbC",
to: "0x88395111AB1586a4030dAC62a183542762929bbC", // first
// to: "0x6ddb3A03366e218956483A840D8BfB490ADE1beB", // second
data: '',
value: transferAmount,
gasLimit: 1000000,
gasPrice: hre.ethers.parseUnits("1", "gwei"),
gasPrice: hre.ethers.parseUnits("4", "gwei"),
});
// 发送交易
......
......@@ -5,14 +5,15 @@ const hre = require("hardhat");
async function test() {
const signer = new hre.ethers.Wallet(process.env.DEPLOY_PRIVATE_KEY, hre.ethers.provider);
const transferAmount = hre.ethers.parseEther("0.0001");
const chainId = await hre.ethers.provider.getNetwork().then(network => network.chainId);
var nonce = await hre.ethers.provider.getTransactionCount(signer.address)
nonce = nonce - 1
const signedTx = await signer.signTransaction({
type: 0,
chainId: 100,
chainId: chainId,
nonce: nonce,
to: "0x88395111AB1586a4030dAC62a183542762929bbC",
to: "0x820b54e2446941213F09d576994B84c32D1ebA2B",
data: '',
value: transferAmount,
gasLimit: 1000000,
......
const { ContractFactory } = require("ethers");
const hre = require("hardhat");
// Define the script
async function main() {
// Deploy the contract
const factory = await hre.ethers.getContractFactory(
"Multicall2"
);
const contract = await factory.deploy();
await contract.waitForDeployment();
const depAddr = await contract.getAddress();
console.log("Contract deployed at:", depAddr);
}
// Run the script
main().catch((error) => {
console.error("Error:", error);
});
\ No newline at end of file
......@@ -7,7 +7,6 @@ async function main() {
const factory = await hre.ethers.getContractFactory(
"RevertContract"
);
// const contract = await factory.deploy({ value: hre.ethers.parseEther("0.0008") });
const contract = await factory.deploy();
await contract.waitForDeployment();
const depAddr = await contract.getAddress();
......
......@@ -5,11 +5,12 @@ const hre = require("hardhat");
async function test() {
const signer = new hre.ethers.Wallet(process.env.DEPLOY_PRIVATE_KEY, hre.ethers.provider);
const transferAmount = hre.ethers.parseEther("0.0001");
const chainId = await hre.ethers.provider.getNetwork().then(network => network.chainId);
var nonce = await hre.ethers.provider.getTransactionCount(signer.address)
const signedTx = await signer.signTransaction({
type: 0,
chainId: 100,
chainId: chainId,
nonce: nonce,
to: "0x88395111AB1586a4030dAC62a183542762929bbC",
data: '',
......
const { ContractFactory } = require("ethers");
const hre = require("hardhat");
var addrlist = [
"0xaccf7aacd00bb765120f10685e58d916d3ec3057",
"0x4631638617F6CF476BeC1AA9A892D308AaE2062A",
"0x43Fa8B58AaBeF30bE4221C7c435Fa38d34f0ccb1",
"0x193b7fBA150679AC96c675964180dA078B73fE3c",
"0x5b89a423416B00769ecB9F148411CD686d165A43",
"0xDF2cf3eE3E4E36490F5f942372fF4c975040bBd0",
"0xdD9720aBe15CD07cc2BF9213C1f08CFf1AE0b37D",
"0xD7FB4128F5451f88A5e525ef5303d714633435A7",
"0xf5c5C8cC784C8183316f4d1f0E50c46DF45920c8",
"0x9923f67774A4250bf2B2eA34Da9cBbc6aBd98caD",
"0xe40701772cCBA7a515CE557c3eaeB4B29B92C71e"
]
async function test() {
const transferAmount = hre.ethers.parseEther("100000");
const signer = new hre.ethers.Wallet(process.env.DEPLOY_PRIVATE_KEY, hre.ethers.provider);
const chainId = await hre.ethers.provider.getNetwork().then(network => network.chainId);
var nonce = await hre.ethers.provider.getTransactionCount(signer.address)
for (let i = 0; i < addrlist.length; i++) {
const signedTx = await signer.signTransaction({
type: 0,
chainId: chainId,
nonce: nonce + i,
to: addrlist[i],
data: '',
value: transferAmount,
gasLimit: 210000,
gasPrice: hre.ethers.parseUnits("1", "gwei"),
});
// 发送交易
const txResponse = await hre.ethers.provider.broadcastTransaction(signedTx);
console.log("Transaction sent to:", addrlist[i], " tx hash:", txResponse.hash);
}
console.log("All transactions sent.");
}
// Define the script
async function main() {
await test()
}
// Run the script
main().catch((error) => {
console.error("Error:", error);
});
\ No newline at end of file
......@@ -7,13 +7,13 @@ async function test() {
const transferAmount = hre.ethers.parseEther("0.0005");
var nonce = await hre.ethers.provider.getTransactionCount(signer.address)
nonce = nonce + 1;
nonce = 27;
const signedTx = await signer.signTransaction({
type: 0,
chainId: 100,
nonce: nonce,
// to: "0x88395111AB1586a4030dAC62a183542762929bbC", // first
to: "0x6ddb3A03366e218956483A840D8BfB490ADE1beB", // second
to: "0x88395111AB1586a4030dAC62a183542762929bbC", // first
// to: "0x6ddb3A03366e218956483A840D8BfB490ADE1beB", // second
data: '',
value: transferAmount,
gasLimit: 1000000,
......
const { ContractFactory } = require("ethers");
const hre = require("hardhat");
async function getTokenInfo(contract) {
const name = await contract.name();
console.log("Token Name:", name);
const symbol = await contract.symbol();
const totalSupply = await contract.totalSupply();
console.log("Token Symbol:", symbol);
console.log("Total Supply:", totalSupply.toString());
}
// Define the script
async function main() {
// Deploy the contract
const factory = await hre.ethers.getContractFactory(
"TestToken"
);
{
const contract = await factory.deploy("TestTokenAlice", "TTA");
await contract.waitForDeployment();
const depAddr = await contract.getAddress();
console.log("TTA deployed at:", depAddr);
await getTokenInfo(contract);
}
{
const contract = await factory.deploy("TestTokenBob", "TTB");
await contract.waitForDeployment();
const depAddr = await contract.getAddress();
console.log("TTB deployed at:", depAddr);
await getTokenInfo(contract);
}
{
const contract = await factory.deploy("TestTokenCharlie", "TTC");
await contract.waitForDeployment();
const depAddr = await contract.getAddress();
console.log("TTC deployed at:", depAddr);
await getTokenInfo(contract);
}
{
const contract = await factory.deploy("TestTokenDoody", "TTD");
await contract.waitForDeployment();
const depAddr = await contract.getAddress();
console.log("TTD deployed at:", depAddr);
await getTokenInfo(contract);
}
}
// Run the script
main().catch((error) => {
console.error("Error:", error);
});
\ No newline at end of file
const { ContractFactory } = require("ethers");
const hre = require("hardhat");
const {ethers} = require("hardhat");
async function deploy() {
[owner] = await ethers.getSigners();
// Deploy the contract
const factory = await hre.ethers.getContractFactory(
"TestAbi"
);
const contract = await factory.deploy(owner.address);
await contract.waitForDeployment();
const depAddr = await contract.getAddress();
console.log("Contract deployed for mova at:", depAddr);
const params = {
toChainID: 2,
receiver: "0x000000000000000000000000000000000000dead",
token: "0x000000000000000000000000000000000000beef",
amount: hre.ethers.parseEther("10"),
outId: 1,
fromChainID: 1,
sender: "0x000000000000000000000000000000000000cafe",
sendToken: "0x000000000000000000000000000000000000babe",
sendAmount: hre.ethers.parseEther("10"),
signature: "0x000000000000000000000000000000000000babe000000000000000000000000",
};
const tx = await contract.getSubmitData(params);
console.log("Computed hash:", tx);
}
async function initForMovaChain(bridge) {
await addValidators(bridge, initialValidators);
}
// Define the script
async function main() {
await deploy();
}
// Run the script
main().catch((error) => {
console.error("Error:", error);
});
\ No newline at end of file
const { ContractFactory } = require("ethers");
const hre = require("hardhat");
async function getTokenInfo(contract) {
const name = await contract.name();
console.log("Token Name:", name);
const symbol = await contract.symbol();
const totalSupply = await contract.totalSupply();
console.log("Token Symbol:", symbol);
console.log("Total Supply:", totalSupply.toString());
}
// Define the script
async function main() {
......@@ -13,6 +23,11 @@ async function main() {
await contract.waitForDeployment();
const depAddr = await contract.getAddress();
console.log("Contract deployed at:", depAddr);
await getTokenInfo(contract);
// const minttx = await contract.mint('0x88395111AB1586a4030dAC62a183542762929bbC', 10000);
// console.log("mint tx:", minttx.hash);
}
// Run the script
......
const { ContractFactory } = require("ethers");
const hre = require("hardhat");
// Define the script
async function main() {
// Deploy the contract
const factory = await hre.ethers.getContractFactory(
"WMDD"
);
const contract = await factory.deploy();
await contract.waitForDeployment();
const depAddr = await contract.getAddress();
console.log("Contract deployed at:", depAddr);
}
// Run the script
main().catch((error) => {
console.error("Error:", error);
});
\ No newline at end of file
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("TestAbi Contract", function () {
let TestAbi, testAbi, owner;
beforeEach(async function () {
// Deploy the TestAbi contract
[owner] = await ethers.getSigners();
const TestAbiFactory = await ethers.getContractFactory("TestAbi");
testAbi = await TestAbiFactory.deploy(owner.address);
await testAbi.waitForDeployment();
});
it("should return the correct keccak256 hash for getSubmitData", async function () {
// Define the input parameters for getSubmitData
const params = {
toChainID: 2,
receiver: "0x000000000000000000000000000000000000dead",
token: "0x000000000000000000000000000000000000beef",
amount: ethers.parseEther("10"),
outId: 1,
fromChainID: 1,
sender: "0x000000000000000000000000000000000000cafe",
sendToken: "0x000000000000000000000000000000000000babe",
sendAmount: ethers.parseEther("10"),
signature: "0x000000000000000000000000000000000000babe000000000000000000000000",
};
// Compute the expected hash
var data = ethers.AbiCoder.defaultAbiCoder().encode(
[
"uint256",
"uint256",
"address",
"address",
"uint256",
"uint256",
"address",
"address",
],
[
params.outId,
params.fromChainID,
params.sender,
params.sendToken,
params.sendAmount,
params.toChainID,
params.receiver,
params.token,
]
);
console.log("Encoded data:", data);
const expectedHash = ethers.keccak256(data);
// Call the getSubmitData function
const result = await testAbi.getSubmitData(params);
console.log("Computed hash:", result);
console.log("Expected hash:", expectedHash);
// Assert that the result matches the expected hash
expect(result).to.equal(expectedHash);
});
});
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment