Transaction Hash:
Block:
19615431 at Apr-09-2024 03:32:47 AM +UTC
Transaction Fee:
0.001007463211955346 ETH
$2.51
Gas Used:
46,038 Gas / 21.883296667 Gwei
Emitted Events:
345 |
Zeta.Transfer( from=[Sender] 0xb23304d73ea53aa80b77f6a29daad2ccfc22c582, to=[Receiver] ZetaConnectorEth, value=2615508890000000000 )
|
346 |
Zeta.Approval( owner=[Sender] 0xb23304d73ea53aa80b77f6a29daad2ccfc22c582, spender=[Receiver] ZetaConnectorEth, value=0 )
|
347 |
ZetaConnectorEth.ZetaSent( sourceTxOriginAddress=[Sender] 0xb23304d73ea53aa80b77f6a29daad2ccfc22c582, zetaTxSenderAddress=[Sender] 0xb23304d73ea53aa80b77f6a29daad2ccfc22c582, destinationChainId=7000, destinationAddress=[Sender] 0xb23304d73ea53aa80b77f6a29daad2ccfc22c582, zetaValueAndGas=2615508890000000000, destinationGasLimit=100000, message=0x, zetaParams=0x )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x4838B106...B0BAD5f97
Miner
| (Titan Builder) | 10.218454532468146037 Eth | 10.218454578506146037 Eth | 0.000000046038 | |
0xb23304d7...Cfc22c582 |
0.001771247591579122 Eth
Nonce: 5
|
0.000763784379623776 Eth
Nonce: 6
| 0.001007463211955346 | ||
0xf091867E...9D82e9cc8 |
Execution Trace
ZetaConnectorEth.send( input=[{name:destinationChainId, type:uint256, order:1, indexed:false, value:7000, valueString:7000}, {name:destinationAddress, type:bytes, order:2, indexed:false, value:0xB23304D73EA53AA80B77F6A29DAAD2CCFC22C582, valueString:0xB23304D73EA53AA80B77F6A29DAAD2CCFC22C582}, {name:destinationGasLimit, type:uint256, order:3, indexed:false, value:100000, valueString:100000}, {name:message, type:bytes, order:4, indexed:false, value:0x, valueString:0x}, {name:zetaValueAndGas, type:uint256, order:5, indexed:false, value:2615508890000000000, valueString:2615508890000000000}, {name:zetaParams, type:bytes, order:6, indexed:false, value:0x, valueString:0x}] )
-
Zeta.transferFrom( sender=0xb23304d73Ea53aa80b77F6a29dAAD2CCfc22c582, recipient=0x000007Cf399229b2f5A4D043F20E90C9C98B7C6a, amount=2615508890000000000 ) => ( True )
send[ZetaConnectorEth (ln:553)]
transferFrom[ZetaConnectorEth (ln:554)]
ZetaTransferError[ZetaConnectorEth (ln:555)]
ZetaSent[ZetaConnectorEth (ln:557)]
File 1 of 2: ZetaConnectorEth
File 2 of 2: Zeta
// Sources flattened with hardhat v2.13.1 https://hardhat.org // File @openzeppelin/contracts/utils/[email protected] // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity 0.8.7; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } } // File @openzeppelin/contracts/security/[email protected] /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { _requireNotPaused(); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { _requirePaused(); _; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Throws if the contract is paused. */ function _requireNotPaused() internal view virtual { require(!paused(), "Pausable: paused"); } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { require(paused(), "Pausable: not paused"); } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } } // File @openzeppelin/contracts/token/ERC20/[email protected] /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 amount) external returns (bool); } // File contracts/evm/interfaces/ConnectorErrors.sol /** * @dev Interface with connector custom errors */ interface ConnectorErrors { // @dev Thrown when caller is not the address defined as paused address error CallerIsNotPauser(address caller); // @dev Thrown when caller is not the address defined as TSS address error CallerIsNotTss(address caller); // @dev Thrown when caller is not the address defined as TSS Updater address error CallerIsNotTssUpdater(address caller); // @dev Thrown when caller is not the address defined as TSS or TSS Updater address error CallerIsNotTssOrUpdater(address caller); // @dev Thrown when Zeta can't be transferred for some reason error ZetaTransferError(); // @dev Thrown when maxSupply will be exceed if minting will proceed error ExceedsMaxSupply(uint256 maxSupply); } // File contracts/evm/interfaces/ZetaInterfaces.sol interface ZetaInterfaces { /** * @dev Use SendInput to interact with the Connector: connector.send(SendInput) */ struct SendInput { /// @dev Chain id of the destination chain. More about chain ids https://docs.zetachain.com/learn/glossary#chain-id uint256 destinationChainId; /// @dev Address receiving the message on the destination chain (expressed in bytes since it can be non-EVM) bytes destinationAddress; /// @dev Gas limit for the destination chain's transaction uint256 destinationGasLimit; /// @dev An encoded, arbitrary message to be parsed by the destination contract bytes message; /// @dev ZETA to be sent cross-chain + ZetaChain gas fees + destination chain gas fees (expressed in ZETA) uint256 zetaValueAndGas; /// @dev Optional parameters for the ZetaChain protocol bytes zetaParams; } /** * @dev Our Connector calls onZetaMessage with this struct as argument */ struct ZetaMessage { bytes zetaTxSenderAddress; uint256 sourceChainId; address destinationAddress; /// @dev Remaining ZETA from zetaValueAndGas after subtracting ZetaChain gas fees and destination gas fees uint256 zetaValue; bytes message; } /** * @dev Our Connector calls onZetaRevert with this struct as argument */ struct ZetaRevert { address zetaTxSenderAddress; uint256 sourceChainId; bytes destinationAddress; uint256 destinationChainId; /// @dev Equals to: zetaValueAndGas - ZetaChain gas fees - destination chain gas fees - source chain revert tx gas fees uint256 remainingZetaValue; bytes message; } } interface ZetaConnector { /** * @dev Sending value and data cross-chain is as easy as calling connector.send(SendInput) */ function send(ZetaInterfaces.SendInput calldata input) external; } interface ZetaReceiver { /** * @dev onZetaMessage is called when a cross-chain message reaches a contract */ function onZetaMessage(ZetaInterfaces.ZetaMessage calldata zetaMessage) external; /** * @dev onZetaRevert is called when a cross-chain message reverts. * It's useful to rollback to the original state */ function onZetaRevert(ZetaInterfaces.ZetaRevert calldata zetaRevert) external; } /** * @dev ZetaTokenConsumer makes it easier to handle the following situations: * - Getting Zeta using native coin (to pay for destination gas while using `connector.send`) * - Getting Zeta using a token (to pay for destination gas while using `connector.send`) * - Getting native coin using Zeta (to return unused destination gas when `onZetaRevert` is executed) * - Getting a token using Zeta (to return unused destination gas when `onZetaRevert` is executed) * @dev The interface can be implemented using different strategies, like UniswapV2, UniswapV3, etc */ interface ZetaTokenConsumer { event EthExchangedForZeta(uint256 amountIn, uint256 amountOut); event TokenExchangedForZeta(address token, uint256 amountIn, uint256 amountOut); event ZetaExchangedForEth(uint256 amountIn, uint256 amountOut); event ZetaExchangedForToken(address token, uint256 amountIn, uint256 amountOut); function getZetaFromEth(address destinationAddress, uint256 minAmountOut) external payable returns (uint256); function getZetaFromToken( address destinationAddress, uint256 minAmountOut, address inputToken, uint256 inputTokenAmount ) external returns (uint256); function getEthFromZeta( address destinationAddress, uint256 minAmountOut, uint256 zetaTokenAmount ) external returns (uint256); function getTokenFromZeta( address destinationAddress, uint256 minAmountOut, address outputToken, uint256 zetaTokenAmount ) external returns (uint256); function hasZetaLiquidity() external view returns (bool); } interface ZetaCommonErrors { error InvalidAddress(); } // File contracts/evm/ZetaConnector.base.sol /** * @dev Main abstraction of ZetaConnector. * This contract manages interactions between TSS and different chains. * There's an instance of this contract on each chain supported by ZetaChain. */ contract ZetaConnectorBase is ConnectorErrors, Pausable { address public immutable zetaToken; /** * @dev Multisig contract to pause incoming transactions. * The responsibility of pausing outgoing transactions is left to the protocol for more flexibility. */ address public pauserAddress; /** * @dev Collectively held by ZetaChain validators. */ address public tssAddress; /** * @dev This address will start pointing to a multisig contract, then it will become the TSS address itself. */ address public tssAddressUpdater; event ZetaSent( address sourceTxOriginAddress, address indexed zetaTxSenderAddress, uint256 indexed destinationChainId, bytes destinationAddress, uint256 zetaValueAndGas, uint256 destinationGasLimit, bytes message, bytes zetaParams ); event ZetaReceived( bytes zetaTxSenderAddress, uint256 indexed sourceChainId, address indexed destinationAddress, uint256 zetaValue, bytes message, bytes32 indexed internalSendHash ); event ZetaReverted( address zetaTxSenderAddress, uint256 sourceChainId, uint256 indexed destinationChainId, bytes destinationAddress, uint256 remainingZetaValue, bytes message, bytes32 indexed internalSendHash ); event TSSAddressUpdated(address callerAddress, address newTssAddress); event TSSAddressUpdaterUpdated(address callerAddress, address newTssUpdaterAddress); event PauserAddressUpdated(address callerAddress, address newTssAddress); /** * @dev Constructor requires initial addresses. * zetaToken address is the only immutable one, while others can be updated. */ constructor(address zetaToken_, address tssAddress_, address tssAddressUpdater_, address pauserAddress_) { if ( zetaToken_ == address(0) || tssAddress_ == address(0) || tssAddressUpdater_ == address(0) || pauserAddress_ == address(0) ) { revert ZetaCommonErrors.InvalidAddress(); } zetaToken = zetaToken_; tssAddress = tssAddress_; tssAddressUpdater = tssAddressUpdater_; pauserAddress = pauserAddress_; } /** * @dev Modifier to restrict actions to pauser address. */ modifier onlyPauser() { if (msg.sender != pauserAddress) revert CallerIsNotPauser(msg.sender); _; } /** * @dev Modifier to restrict actions to TSS address. */ modifier onlyTssAddress() { if (msg.sender != tssAddress) revert CallerIsNotTss(msg.sender); _; } /** * @dev Modifier to restrict actions to TSS updater address. */ modifier onlyTssUpdater() { if (msg.sender != tssAddressUpdater) revert CallerIsNotTssUpdater(msg.sender); _; } /** * @dev Update the pauser address. The only address allowed to do that is the current pauser. */ function updatePauserAddress(address pauserAddress_) external onlyPauser { if (pauserAddress_ == address(0)) revert ZetaCommonErrors.InvalidAddress(); pauserAddress = pauserAddress_; emit PauserAddressUpdated(msg.sender, pauserAddress_); } /** * @dev Update the TSS address. The address can be updated by the TSS updater or the TSS address itself. */ function updateTssAddress(address tssAddress_) external { if (msg.sender != tssAddress && msg.sender != tssAddressUpdater) revert CallerIsNotTssOrUpdater(msg.sender); if (tssAddress_ == address(0)) revert ZetaCommonErrors.InvalidAddress(); tssAddress = tssAddress_; emit TSSAddressUpdated(msg.sender, tssAddress_); } /** * @dev Changes the ownership of tssAddressUpdater to be the one held by the ZetaChain TSS Signer nodes. */ function renounceTssAddressUpdater() external onlyTssUpdater { if (tssAddress == address(0)) revert ZetaCommonErrors.InvalidAddress(); tssAddressUpdater = tssAddress; emit TSSAddressUpdaterUpdated(msg.sender, tssAddressUpdater); } /** * @dev Pause the input (send) transactions. */ function pause() external onlyPauser { _pause(); } /** * @dev Unpause the contract to allow transactions again. */ function unpause() external onlyPauser { _unpause(); } /** * @dev Entrypoint to send data and value through ZetaChain. */ function send(ZetaInterfaces.SendInput calldata input) external virtual {} /** * @dev Handler to receive data from other chain. * This method can be called only by TSS. Access validation is in implementation. */ function onReceive( bytes calldata zetaTxSenderAddress, uint256 sourceChainId, address destinationAddress, uint256 zetaValue, bytes calldata message, bytes32 internalSendHash ) external virtual {} /** * @dev Handler to receive errors from other chain. * This method can be called only by TSS. Access validation is in implementation. */ function onRevert( address zetaTxSenderAddress, uint256 sourceChainId, bytes calldata destinationAddress, uint256 destinationChainId, uint256 remainingZetaValue, bytes calldata message, bytes32 internalSendHash ) external virtual {} } // File contracts/evm/ZetaConnector.eth.sol /** * @dev ETH implementation of ZetaConnector. * This contract manages interactions between TSS and different chains. * This version is only for Ethereum network because in the other chains we mint and burn and in this one we lock and unlock. */ contract ZetaConnectorEth is ZetaConnectorBase { constructor( address zetaToken_, address tssAddress_, address tssAddressUpdater_, address pauserAddress_ ) ZetaConnectorBase(zetaToken_, tssAddress_, tssAddressUpdater_, pauserAddress_) {} function getLockedAmount() external view returns (uint256) { return IERC20(zetaToken).balanceOf(address(this)); } /** * @dev Entrypoint to send data through ZetaChain * This call locks the token on the contract and emits an event with all the data needed by the protocol. */ function send(ZetaInterfaces.SendInput calldata input) external override whenNotPaused { bool success = IERC20(zetaToken).transferFrom(msg.sender, address(this), input.zetaValueAndGas); if (!success) revert ZetaTransferError(); emit ZetaSent( tx.origin, msg.sender, input.destinationChainId, input.destinationAddress, input.zetaValueAndGas, input.destinationGasLimit, input.message, input.zetaParams ); } /** * @dev Handler to receive data from other chain. * This method can be called only by TSS. * Transfers the Zeta tokens to destination and calls onZetaMessage if it's needed. */ function onReceive( bytes calldata zetaTxSenderAddress, uint256 sourceChainId, address destinationAddress, uint256 zetaValue, bytes calldata message, bytes32 internalSendHash ) external override onlyTssAddress { bool success = IERC20(zetaToken).transfer(destinationAddress, zetaValue); if (!success) revert ZetaTransferError(); if (message.length > 0) { ZetaReceiver(destinationAddress).onZetaMessage( ZetaInterfaces.ZetaMessage(zetaTxSenderAddress, sourceChainId, destinationAddress, zetaValue, message) ); } emit ZetaReceived(zetaTxSenderAddress, sourceChainId, destinationAddress, zetaValue, message, internalSendHash); } /** * @dev Handler to receive errors from other chain. * This method can be called only by TSS. * Transfers the Zeta tokens to destination and calls onZetaRevert if it's needed. */ function onRevert( address zetaTxSenderAddress, uint256 sourceChainId, bytes calldata destinationAddress, uint256 destinationChainId, uint256 remainingZetaValue, bytes calldata message, bytes32 internalSendHash ) external override whenNotPaused onlyTssAddress { bool success = IERC20(zetaToken).transfer(zetaTxSenderAddress, remainingZetaValue); if (!success) revert ZetaTransferError(); if (message.length > 0) { ZetaReceiver(zetaTxSenderAddress).onZetaRevert( ZetaInterfaces.ZetaRevert( zetaTxSenderAddress, sourceChainId, destinationAddress, destinationChainId, remainingZetaValue, message ) ); } emit ZetaReverted( zetaTxSenderAddress, sourceChainId, destinationChainId, destinationAddress, remainingZetaValue, message, internalSendHash ); } }
File 2 of 2: Zeta
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } } /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address recipient, 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 sender, address recipient, 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); } interface IERC20Metadata is IERC20 { function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); } contract ERC20 is Context, IERC20, IERC20Metadata { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } function name() public view virtual override returns (string memory) { return _name; } function symbol() public view virtual override returns (string memory) { return _symbol; } function decimals() public view virtual override returns (uint8) { return 18; } function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } function transferFrom(address sender,address recipient,uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); uint256 currentAllowance = _allowances[sender][_msgSender()]; require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance"); _approve(sender, _msgSender(), currentAllowance - amount); return true; } function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); uint256 senderBalance = _balances[sender]; require(senderBalance >= amount, "ERC20: transfer amount exceeds balance"); _balances[sender] = senderBalance - amount; _balances[recipient] += amount; emit Transfer(sender, recipient, amount); } function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _totalSupply += amount; _balances[account] += amount; emit Transfer(address(0), account, amount); } function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); _balances[account] = accountBalance - amount; _totalSupply -= amount; emit Transfer(account, address(0), amount); } function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } } contract Zeta is ERC20 { constructor(uint256 initialSupply, string memory name, string memory symbol) ERC20(name, symbol) { _mint(msg.sender, initialSupply * (10 ** uint256(decimals()))); } }