Feature Tip: Add private address tag to any address under My Name Tag !
More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 1,066 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Wrap And Transfe... | 18775651 | 381 days ago | IN | 0.1 ETH | 0.00567852 | ||||
Transfer Tokens ... | 18608628 | 405 days ago | IN | 0 ETH | 0.00641009 | ||||
Transfer Tokens ... | 18386499 | 436 days ago | IN | 0 ETH | 0.00221982 | ||||
Wrap And Transfe... | 18190301 | 463 days ago | IN | 0.0098 ETH | 0.00129971 | ||||
Transfer Tokens ... | 17822560 | 515 days ago | IN | 0 ETH | 0.00445184 | ||||
Wrap And Transfe... | 17755032 | 524 days ago | IN | 0.01 ETH | 0.00194558 | ||||
Transfer Tokens ... | 17720550 | 529 days ago | IN | 0 ETH | 0.00508453 | ||||
Transfer Tokens ... | 17659482 | 538 days ago | IN | 0 ETH | 0.00289898 | ||||
Transfer Tokens ... | 17659151 | 538 days ago | IN | 0 ETH | 0.00218733 | ||||
Wrap And Transfe... | 17606407 | 545 days ago | IN | 0.02 ETH | 0.00202237 | ||||
Transfer Tokens ... | 17596236 | 546 days ago | IN | 0 ETH | 0.00413137 | ||||
Wrap And Transfe... | 17595815 | 546 days ago | IN | 0.0008 ETH | 0.0025777 | ||||
Complete Transfe... | 17577106 | 549 days ago | IN | 0 ETH | 0.04064039 | ||||
Wrap And Transfe... | 17544896 | 554 days ago | IN | 0.015 ETH | 0.00400751 | ||||
Wrap And Transfe... | 17531856 | 555 days ago | IN | 0.1 ETH | 0.00257144 | ||||
Wrap And Transfe... | 17529953 | 556 days ago | IN | 0.01 ETH | 0.00378762 | ||||
Wrap And Transfe... | 17526673 | 556 days ago | IN | 0.01 ETH | 0.00198398 | ||||
Wrap And Transfe... | 17518467 | 557 days ago | IN | 0.002 ETH | 0.00194682 | ||||
Wrap And Transfe... | 17517327 | 557 days ago | IN | 0.035 ETH | 0.00172183 | ||||
Transfer Tokens ... | 17516289 | 558 days ago | IN | 0 ETH | 0.00256019 | ||||
Transfer Tokens ... | 17473458 | 564 days ago | IN | 0 ETH | 0.00239302 | ||||
Wrap And Transfe... | 17466231 | 565 days ago | IN | 0.007 ETH | 0.00289817 | ||||
Wrap And Transfe... | 17462895 | 565 days ago | IN | 0.1 ETH | 0.00219108 | ||||
Wrap And Transfe... | 17462856 | 565 days ago | IN | 0.009 ETH | 0.00210367 | ||||
Transfer Tokens ... | 17461473 | 565 days ago | IN | 0 ETH | 0.00289578 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
18775651 | 381 days ago | 0.1 ETH | ||||
18190301 | 463 days ago | 0.0098 ETH | ||||
17755032 | 524 days ago | 0.01 ETH | ||||
17606407 | 545 days ago | 0.02 ETH | ||||
17595815 | 546 days ago | 0.0008 ETH | ||||
17544896 | 554 days ago | 0.015 ETH | ||||
17531856 | 555 days ago | 0.1 ETH | ||||
17529953 | 556 days ago | 0.01 ETH | ||||
17526673 | 556 days ago | 0.01 ETH | ||||
17518467 | 557 days ago | 0.002 ETH | ||||
17517327 | 557 days ago | 0.035 ETH | ||||
17466231 | 565 days ago | 0.007 ETH | ||||
17462895 | 565 days ago | 0.1 ETH | ||||
17462856 | 565 days ago | 0.009 ETH | ||||
17458874 | 566 days ago | 0.001 ETH | ||||
17458649 | 566 days ago | 0.7 ETH | ||||
17435368 | 569 days ago | 0.025 ETH | ||||
17420034 | 571 days ago | 0.0055 ETH | ||||
17413430 | 572 days ago | 0.0025 ETH | ||||
17408660 | 573 days ago | 0.2 ETH | ||||
17407518 | 573 days ago | 0.011652 ETH | ||||
17402595 | 574 days ago | 0.02 ETH | ||||
17401101 | 574 days ago | 0.05 ETH | ||||
17393190 | 575 days ago | 0.07 ETH | ||||
17386559 | 576 days ago | 0.005 ETH |
Loading...
Loading
Contract Name:
TokenBridgeRelayer
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.17; import {IWormhole} from "../interfaces/IWormhole.sol"; import "../libraries/BytesLib.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "./TokenBridgeRelayerGovernance.sol"; import "./TokenBridgeRelayerMessages.sol"; /** * @title Wormhole Token Bridge Relayer * @notice This contract composes on Wormhole's Token Bridge contracts to faciliate * one-click transfers of Token Bridge supported assets cross chain. */ contract TokenBridgeRelayer is TokenBridgeRelayerGovernance, TokenBridgeRelayerMessages, ReentrancyGuard { using BytesLib for bytes; constructor( uint16 chainId, address wormhole, address tokenBridge_, address wethAddress, bool unwrapWeth_ ) { require(chainId > 0, "invalid chainId"); require(wormhole != address(0), "invalid wormhole address"); require(tokenBridge_ != address(0), "invalid token bridge address"); require(wethAddress != address(0), "invalid weth address"); // set initial state setOwner(msg.sender); setChainId(chainId); setWormhole(wormhole); setTokenBridge(tokenBridge_); setWethAddress(wethAddress); setUnwrapWethFlag(unwrapWeth_); // set the initial swapRate/relayer precisions to 1e8 setSwapRatePrecision(1e8); setRelayerFeePrecision(1e8); } /** * @notice Emitted when a transfer is completed by the Wormhole token bridge * @param emitterChainId Wormhole chain ID of emitter contract on the source chain * @param emitterAddress Address (bytes32 zero-left-padded) of emitter on the source chain * @param sequence Sequence of the Wormhole message */ event TransferRedeemed( uint16 indexed emitterChainId, bytes32 indexed emitterAddress, uint64 indexed sequence ); /** * @notice Emitted when a swap is executed with an off-chain relayer * @param recipient Address of the recipient of the native assets * @param relayer Address of the relayer that performed the swap * @param token Address of the token being swapped * @param tokenAmount Amount of token being swapped * @param nativeAmount Amount of native assets swapped for tokens */ event SwapExecuted( address indexed recipient, address indexed relayer, address indexed token, uint256 tokenAmount, uint256 nativeAmount ); /** * @notice Calls Wormhole's Token Bridge contract to emit a contract-controlled * transfer. The transfer message includes an arbitrary payload with instructions * for how to handle relayer payments on the target contract and the quantity of * tokens to convert into native assets for the user. * @param token ERC20 token address to transfer cross chain. * @param amount Quantity of tokens to be transferred. * @param toNativeTokenAmount Amount of tokens to swap into native assets on * the target chain. * @param targetChain Wormhole chain ID of the target blockchain. * @param targetRecipient User's wallet address on the target blockchain in bytes32 format * (zero-left-padded). * @param batchId ID for Wormhole message batching * @return messageSequence Wormhole sequence for emitted TransferTokensWithRelay message. */ function transferTokensWithRelay( address token, uint256 amount, uint256 toNativeTokenAmount, uint16 targetChain, bytes32 targetRecipient, uint32 batchId ) public payable nonReentrant returns (uint64 messageSequence) { // Cache wormhole fee and confirm that the user has passed enough // value to cover the wormhole protocol fee. uint256 wormholeFee = wormhole().messageFee(); require(msg.value == wormholeFee, "insufficient value"); // Cache token decimals, and remove dust from the amount argument. This // ensures that the dust is never transferred to this contract. uint8 tokenDecimals = getDecimals(token); amount = denormalizeAmount( normalizeAmount(amount, tokenDecimals), tokenDecimals ); // Transfer tokens from user to the this contract, and // override amount with actual amount received. amount = custodyTokens(token, amount); // call the internal _transferTokensWithRelay function messageSequence = _transferTokensWithRelay( InternalTransferParams({ token: token, amount: amount, tokenDecimals: tokenDecimals, toNativeTokenAmount: toNativeTokenAmount, targetChain: targetChain, targetRecipient: targetRecipient }), batchId, wormholeFee ); } /** * @notice Wraps Ether and calls Wormhole's Token Bridge contract to emit * a contract-controlled transfer. The transfer message includes an arbitrary * payload with instructions for how to handle relayer payments on the target * contract and the quantity of tokens to convert into native assets for the user. * @param toNativeTokenAmount Amount of tokens to swap into native assets on * the target chain. * @param targetChain Wormhole chain ID of the target blockchain. * @param targetRecipient User's wallet address on the target blockchain in bytes32 format * (zero-left-padded). * @param batchId ID for Wormhole message batching * @return messageSequence Wormhole sequence for emitted TransferTokensWithRelay message. */ function wrapAndTransferEthWithRelay( uint256 toNativeTokenAmount, uint16 targetChain, bytes32 targetRecipient, uint32 batchId ) public payable returns (uint64 messageSequence) { require(unwrapWeth(), "WETH functionality not supported"); // Cache wormhole fee and confirm that the user has passed enough // value to cover the wormhole protocol fee. uint256 wormholeFee = wormhole().messageFee(); require(msg.value > wormholeFee, "insufficient value"); // remove the wormhole protocol fee from the amount uint256 amount = msg.value - wormholeFee; // refund dust uint256 dust = amount - denormalizeAmount(normalizeAmount(amount, 18), 18); if (dust > 0) { payable(msg.sender).transfer(dust); } // remove dust from amount and cache WETH uint256 amountLessDust = amount - dust; IWETH weth = WETH(); // deposit into the WETH contract weth.deposit{ value : amountLessDust }(); // call the internal _transferTokensWithRelay function messageSequence = _transferTokensWithRelay( InternalTransferParams({ token: address(weth), tokenDecimals: 18, amount: amountLessDust, toNativeTokenAmount: toNativeTokenAmount, targetChain: targetChain, targetRecipient: targetRecipient }), batchId, wormholeFee ); } function _transferTokensWithRelay( InternalTransferParams memory params, uint32 batchId, uint256 wormholeFee ) internal returns (uint64 messageSequence) { // sanity check function arguments require(isAcceptedToken(params.token), "token not accepted"); require( params.targetRecipient != bytes32(0), "targetRecipient cannot be bytes32(0)" ); /** * Cache the normalized amount and verify that it's nonzero. * The token bridge peforms the same operation before encoding * the amount in the `TransferWithPayload` message. */ uint256 normalizedAmount = normalizeAmount( params.amount, params.tokenDecimals ); require(normalizedAmount > 0, "normalized amount must be > 0"); // normalized toNativeTokenAmount should be nonzero uint256 normalizedToNativeTokenAmount = normalizeAmount( params.toNativeTokenAmount, params.tokenDecimals ); require( params.toNativeTokenAmount == 0 || normalizedToNativeTokenAmount > 0, "invalid toNativeTokenAmount" ); // Cache the target contract address and verify that there // is a registered contract for the specified targetChain. bytes32 targetContract = getRegisteredContract(params.targetChain); require(targetContract != bytes32(0), "target not registered"); // Confirm that the user has sent enough tokens to cover the native swap // on the target chain and to pay the relayer fee. uint256 normalizedRelayerFee = normalizeAmount( calculateRelayerFee( params.targetChain, params.token, params.tokenDecimals ), params.tokenDecimals ); require( normalizedAmount > normalizedRelayerFee + normalizedToNativeTokenAmount, "insufficient amount" ); /** * Encode instructions (TransferWithRelay) to send with the token transfer. * The `targetRecipient` address is in bytes32 format (zero-left-padded) to * support non-evm smart contracts that have addresses that are longer * than 20 bytes. * * We normalize the relayerFee and toNativeTokenAmount to support * non-evm smart contracts that can only handle uint64.max values. */ bytes memory messagePayload = encodeTransferWithRelay( TransferWithRelay({ payloadId: 1, targetRelayerFee: normalizedRelayerFee, toNativeTokenAmount: normalizedToNativeTokenAmount, targetRecipient: params.targetRecipient }) ); // cache TokenBridge instance ITokenBridge bridge = tokenBridge(); // approve the token bridge to spend the specified tokens SafeERC20.safeApprove( IERC20(params.token), address(bridge), params.amount ); /** * Call `transferTokensWithPayload` method on the token bridge and pay * the Wormhole network fee. The token bridge will emit a Wormhole * message with an encoded `TransferWithPayload` struct (see the * ITokenBridge.sol interface file in this repo). */ messageSequence = bridge.transferTokensWithPayload{value: wormholeFee}( params.token, params.amount, params.targetChain, targetContract, batchId, messagePayload ); } /** * @notice Calls Wormhole's Token Bridge contract to complete token transfers. Takes * custody of the wrapped (or released) tokens and sends the tokens to the target recipient. * It pays the relayer in the minted token denomination. If requested by the user, * it will perform a swap with the off-chain relayer to provide the user with native assets. * If the `token` being transferred is WETH, the contract will unwrap native assets and send * the transferred amount to the recipient and pay the relayer in native assets. * @dev reverts if: * - the transferred token is not accepted by this contract * - the transffered token is not attested on this blockchain's Token Bridge contract * - the emitter of the transfer message is not registered with this contract * - the relayer fails to provide enough native assets to faciliate a native swap * - the recipient attempts to swap native assets when performing a self redemption * @param encodedTransferMessage Attested `TransferWithPayload` wormhole message. */ function completeTransferWithRelay(bytes calldata encodedTransferMessage) public payable { // complete the transfer by calling the token bridge (bytes memory payload, uint256 amount, address token) = _completeTransfer(encodedTransferMessage); // parse the payload into the `TransferWithRelay` struct TransferWithRelay memory transferWithRelay = decodeTransferWithRelay( payload ); // cache the recipient address and unwrap weth flag address recipient = bytes32ToAddress(transferWithRelay.targetRecipient); bool unwrapWeth = unwrapWeth(); // handle self redemptions if (msg.sender == recipient) { _completeSelfRedemption( token, recipient, amount, unwrapWeth ); // bail out return; } // cache token decimals uint8 tokenDecimals = getDecimals(token); // denormalize the encoded relayerFee transferWithRelay.targetRelayerFee = denormalizeAmount( transferWithRelay.targetRelayerFee, tokenDecimals ); // unwrap and transfer ETH if (token == address(WETH())) { _completeWethTransfer( amount, recipient, transferWithRelay.targetRelayerFee, unwrapWeth ); // bail out return; } // handle native asset payments and refunds if (transferWithRelay.toNativeTokenAmount > 0) { // denormalize the toNativeTokenAmount transferWithRelay.toNativeTokenAmount = denormalizeAmount( transferWithRelay.toNativeTokenAmount, tokenDecimals ); /** * Compute the maximum amount of tokens that the user is allowed * to swap for native assets. * * Override the toNativeTokenAmount in transferWithRelay if the * toNativeTokenAmount is greater than the maxToNativeAllowed. * * Compute the amount of native assets to send the recipient. */ uint256 nativeAmountForRecipient; uint256 maxToNativeAllowed = calculateMaxSwapAmountIn(token); if (transferWithRelay.toNativeTokenAmount > maxToNativeAllowed) { transferWithRelay.toNativeTokenAmount = maxToNativeAllowed; } // compute amount of native asset to pay the recipient nativeAmountForRecipient = calculateNativeSwapAmountOut( token, transferWithRelay.toNativeTokenAmount ); /** * The nativeAmountForRecipient can be zero if the user specifed * a toNativeTokenAmount that is too little to convert to native * asset. We need to override the toNativeTokenAmount to be zero * if that is the case, that way the user receives the full amount * of transferred tokens. */ if (nativeAmountForRecipient > 0) { // check to see if the relayer sent enough value require( msg.value >= nativeAmountForRecipient, "insufficient native asset amount" ); // refund excess native asset to relayer if applicable uint256 relayerRefund = msg.value - nativeAmountForRecipient; if (relayerRefund > 0) { payable(msg.sender).transfer(relayerRefund); } // send requested native asset to target recipient payable(recipient).transfer(nativeAmountForRecipient); // emit swap event emit SwapExecuted( recipient, msg.sender, token, transferWithRelay.toNativeTokenAmount, nativeAmountForRecipient ); } else { // override the toNativeTokenAmount in transferWithRelay transferWithRelay.toNativeTokenAmount = 0; // refund the relayer any native asset sent to this contract if (msg.value > 0) { payable(msg.sender).transfer(msg.value); } } } // add the token swap amount to the relayer fee uint256 amountForRelayer = transferWithRelay.targetRelayerFee + transferWithRelay.toNativeTokenAmount; // pay the relayer if amountForRelayer > 0 if (amountForRelayer > 0) { SafeERC20.safeTransfer( IERC20(token), msg.sender, amountForRelayer ); } // pay the target recipient the remaining tokens SafeERC20.safeTransfer( IERC20(token), recipient, amount - amountForRelayer ); } function _completeTransfer( bytes memory encodedTransferMessage ) internal returns (bytes memory, uint256, address) { /** * parse the encoded Wormhole message * * SECURITY: This message not been verified by the Wormhole core layer yet. * The encoded payload can only be trusted once the message has been verified * by the Wormhole core contract. In this case, the message will be verified * by a call to the token bridge contract in subsequent actions. */ IWormhole.VM memory parsedMessage = wormhole().parseVM( encodedTransferMessage ); /** * The amount encoded in the payload could be incorrect, * since fee-on-transfer tokens are supported by the token bridge. * * NOTE: The token bridge truncates the encoded amount for any token * with decimals greater than 8. This is to support blockchains that * cannot handle transfer amounts exceeding max(uint64). */ address localTokenAddress = fetchLocalAddressFromTransferMessage( parsedMessage.payload ); require(isAcceptedToken(localTokenAddress), "token not registered"); // check balance before completing the transfer uint256 balanceBefore = getBalance(localTokenAddress); // cache the token bridge instance ITokenBridge bridge = tokenBridge(); /** * Call `completeTransferWithPayload` on the token bridge. This * method acts as a reentrancy protection since it does not allow * transfers to be redeemed more than once. */ bytes memory transferPayload = bridge.completeTransferWithPayload( encodedTransferMessage ); // compute and save the balance difference after completing the transfer uint256 amountReceived = getBalance(localTokenAddress) - balanceBefore; // parse the wormhole message payload into the `TransferWithPayload` struct ITokenBridge.TransferWithPayload memory transfer = bridge.parseTransferWithPayload(transferPayload); // confirm that the message sender is a registered TokenBridgeRelayer contract require( transfer.fromAddress == getRegisteredContract(parsedMessage.emitterChainId), "contract not registered" ); // emit event with information about the TransferWithPayload message emit TransferRedeemed( parsedMessage.emitterChainId, parsedMessage.emitterAddress, parsedMessage.sequence ); return ( transfer.payload, amountReceived, localTokenAddress ); } function _completeSelfRedemption( address token, address recipient, uint256 amount, bool unwrapWeth ) internal { // revert if the caller sends ether to this contract require(msg.value == 0, "recipient cannot swap native assets"); // cache WETH instance IWETH weth = WETH(); // transfer the full amount to the recipient if (token == address(weth) && unwrapWeth) { // withdraw weth and send to the recipient weth.withdraw(amount); payable(recipient).transfer(amount); } else { SafeERC20.safeTransfer( IERC20(token), recipient, amount ); } } function _completeWethTransfer( uint256 amount, address recipient, uint256 relayerFee, bool unwrapWeth ) internal { // revert if the relayer sends ether to this contract require(msg.value == 0, "value must be zero"); /** * Check if the weth is unwrappable. Some wrapped native assets * are not unwrappable (e.g. CELO) and must be transferred via * the ERC20 interface. */ if (unwrapWeth) { // withdraw eth WETH().withdraw(amount); // transfer eth to recipient payable(recipient).transfer(amount - relayerFee); // transfer relayer fee to the caller if (relayerFee > 0) { payable(msg.sender).transfer(relayerFee); } } else { // cache WETH instance IWETH weth = WETH(); // transfer the native asset to the caller SafeERC20.safeTransfer( IERC20(address(weth)), recipient, amount - relayerFee ); // transfer relayer fee to the caller if (relayerFee > 0) { SafeERC20.safeTransfer( IERC20(address(weth)), msg.sender, relayerFee ); } } } /** * @notice Parses the encoded address and chainId from a `TransferWithPayload` * message. Finds the address of the wrapped token contract if the token is not * native to this chain. * @param payload Encoded `TransferWithPayload` message * @return localAddress Address of the encoded (bytes32 format) token address on * this chain. */ function fetchLocalAddressFromTransferMessage( bytes memory payload ) public view returns (address localAddress) { // parse the source token address and chainId bytes32 sourceAddress = payload.toBytes32(33); uint16 tokenChain = payload.toUint16(65); // Fetch the wrapped address from the token bridge if the token // is not from this chain. if (tokenChain != chainId()) { // identify wormhole token bridge wrapper localAddress = tokenBridge().wrappedAsset(tokenChain, sourceAddress); require(localAddress != address(0), "token not attested"); } else { // return the encoded address if the token is native to this chain localAddress = bytes32ToAddress(sourceAddress); } } /** * @notice Calculates the max amount of tokens the user can convert to * native assets on this chain. * @dev The max amount of native assets the contract will swap with the user * is governed by the `maxNativeSwapAmount` state variable. * @param token Address of token being transferred. * @return maxAllowed The maximum number of tokens the user is allowed to * swap for native assets. */ function calculateMaxSwapAmountIn( address token ) public view returns (uint256 maxAllowed) { // fetch the decimals for the token and native token uint8 tokenDecimals = getDecimals(token); uint8 nativeDecimals = getDecimals(address(WETH())); if (tokenDecimals > nativeDecimals) { maxAllowed = maxNativeSwapAmount(token) * nativeSwapRate(token) * 10 ** (tokenDecimals - nativeDecimals) / swapRatePrecision(); } else { maxAllowed = (maxNativeSwapAmount(token) * nativeSwapRate(token)) / (10 ** (nativeDecimals - tokenDecimals) * swapRatePrecision()); } } /** * @notice Calculates the amount of native assets that a user will receive * when swapping transferred tokens for native assets. * @param token Address of token being transferred. * @param toNativeAmount Quantity of tokens to be converted to native assets. * @return nativeAmount The exchange rate between native assets and the `toNativeAmount` * of transferred tokens. */ function calculateNativeSwapAmountOut( address token, uint256 toNativeAmount ) public view returns (uint256 nativeAmount) { // fetch the decimals for the token and native token uint8 tokenDecimals = getDecimals(token); uint8 nativeDecimals = getDecimals(address(WETH())); if (tokenDecimals > nativeDecimals) { nativeAmount = swapRatePrecision() * toNativeAmount / (nativeSwapRate(token) * 10 ** (tokenDecimals - nativeDecimals)); } else { nativeAmount = swapRatePrecision() * toNativeAmount * 10 ** (nativeDecimals - tokenDecimals) / nativeSwapRate(token); } } /** * @notice Converts the USD denominated relayer fee into the specified token * denomination. * @param targetChainId Wormhole chain ID of the target blockchain. * @param token Address of token being transferred. * @param decimals Token decimals of token being transferred. * @return feeInTokenDenomination Relayer fee denominated in tokens. */ function calculateRelayerFee( uint16 targetChainId, address token, uint8 decimals ) public view returns (uint256 feeInTokenDenomination) { // cache swap rate uint256 tokenSwapRate = swapRate(token); require(tokenSwapRate != 0, "swap rate not set"); feeInTokenDenomination = 10 ** decimals * relayerFee(targetChainId) * swapRatePrecision() / (tokenSwapRate * relayerFeePrecision()); } function custodyTokens( address token, uint256 amount ) internal returns (uint256) { // query own token balance before transfer uint256 balanceBefore = getBalance(token); // deposit tokens SafeERC20.safeTransferFrom( IERC20(token), msg.sender, address(this), amount ); // return the balance difference return getBalance(token) - balanceBefore; } function bytes32ToAddress(bytes32 address_) internal pure returns (address) { require(bytes12(address_) == 0, "invalid EVM address"); return address(uint160(uint256(address_))); } // necessary for receiving native assets receive() external payable {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @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); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/draft-IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @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 SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 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( IERC20 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( IERC20 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( IERC20 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)); } } function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @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(IERC20 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"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @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 functionCallWithValue(target, data, 0, "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"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, 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) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, 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) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or 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 { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // 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 /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; import "./IWETH.sol"; import "./IWormhole.sol"; interface ITokenBridge { struct Transfer { uint8 payloadID; uint256 amount; bytes32 tokenAddress; uint16 tokenChain; bytes32 to; uint16 toChain; uint256 fee; } struct TransferWithPayload { uint8 payloadID; uint256 amount; bytes32 tokenAddress; uint16 tokenChain; bytes32 to; uint16 toChain; bytes32 fromAddress; bytes payload; } struct AssetMeta { uint8 payloadID; bytes32 tokenAddress; uint16 tokenChain; uint8 decimals; bytes32 symbol; bytes32 name; } struct RegisterChain { bytes32 module; uint8 action; uint16 chainId; uint16 emitterChainID; bytes32 emitterAddress; } struct UpgradeContract { bytes32 module; uint8 action; uint16 chainId; bytes32 newContract; } struct RecoverChainId { bytes32 module; uint8 action; uint256 evmChainId; uint16 newChainId; } event ContractUpgraded(address indexed oldContract, address indexed newContract); function _parseTransferCommon(bytes memory encoded) external pure returns (Transfer memory transfer); function attestToken(address tokenAddress, uint32 nonce) external payable returns (uint64 sequence); function wrapAndTransferETH(uint16 recipientChain, bytes32 recipient, uint256 arbiterFee, uint32 nonce) external payable returns (uint64 sequence); function wrapAndTransferETHWithPayload(uint16 recipientChain, bytes32 recipient, uint32 nonce, bytes memory payload) external payable returns (uint64 sequence); function transferTokens(address token, uint256 amount, uint16 recipientChain, bytes32 recipient, uint256 arbiterFee, uint32 nonce) external payable returns (uint64 sequence); function transferTokensWithPayload(address token, uint256 amount, uint16 recipientChain, bytes32 recipient, uint32 nonce, bytes memory payload) external payable returns (uint64 sequence); function updateWrapped(bytes memory encodedVm) external returns (address token); function createWrapped(bytes memory encodedVm) external returns (address token); function completeTransferWithPayload(bytes memory encodedVm) external returns (bytes memory); function completeTransferAndUnwrapETHWithPayload(bytes memory encodedVm) external returns (bytes memory); function completeTransfer(bytes memory encodedVm) external; function completeTransferAndUnwrapETH(bytes memory encodedVm) external; function encodeAssetMeta(AssetMeta memory meta) external pure returns (bytes memory encoded); function encodeTransfer(Transfer memory transfer) external pure returns (bytes memory encoded); function encodeTransferWithPayload(TransferWithPayload memory transfer) external pure returns (bytes memory encoded); function parsePayloadID(bytes memory encoded) external pure returns (uint8 payloadID); function parseAssetMeta(bytes memory encoded) external pure returns (AssetMeta memory meta); function parseTransfer(bytes memory encoded) external pure returns (Transfer memory transfer); function parseTransferWithPayload(bytes memory encoded) external pure returns (TransferWithPayload memory transfer); function governanceActionIsConsumed(bytes32 hash) external view returns (bool); function isInitialized(address impl) external view returns (bool); function isTransferCompleted(bytes32 hash) external view returns (bool); function wormhole() external view returns (IWormhole); function chainId() external view returns (uint16); function evmChainId() external view returns (uint256); function isFork() external view returns (bool); function governanceChainId() external view returns (uint16); function governanceContract() external view returns (bytes32); function wrappedAsset(uint16 tokenChainId, bytes32 tokenAddress) external view returns (address); function bridgeContracts(uint16 chainId_) external view returns (bytes32); function tokenImplementation() external view returns (address); function WETH() external view returns (IWETH); function outstandingBridged(address token) external view returns (uint256); function isWrappedAsset(address token) external view returns (bool); function finality() external view returns (uint8); function implementation() external view returns (address); function initialize() external; function registerChain(bytes memory encodedVM) external; function upgrade(bytes memory encodedVM) external; function submitRecoverChainId(bytes memory encodedVM) external; function parseRegisterChain(bytes memory encoded) external pure returns (RegisterChain memory chain); function parseUpgrade(bytes memory encoded) external pure returns (UpgradeContract memory chain); function parseRecoverChainId(bytes memory encodedRecoverChainId) external pure returns (RecoverChainId memory rci); }
// contracts/Bridge.sol // SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IWETH is IERC20 { function deposit() external payable; function withdraw(uint amount) external; function balanceOf() external returns (uint256); }
// contracts/Messages.sol // SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; interface IWormhole { struct GuardianSet { address[] keys; uint32 expirationTime; } struct Signature { bytes32 r; bytes32 s; uint8 v; uint8 guardianIndex; } struct VM { uint8 version; uint32 timestamp; uint32 nonce; uint16 emitterChainId; bytes32 emitterAddress; uint64 sequence; uint8 consistencyLevel; bytes payload; uint32 guardianSetIndex; Signature[] signatures; bytes32 hash; } event LogMessagePublished(address indexed sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel); function publishMessage( uint32 nonce, bytes memory payload, uint8 consistencyLevel ) external payable returns (uint64 sequence); function parseAndVerifyVM(bytes calldata encodedVM) external view returns (VM memory vm, bool valid, string memory reason); function verifyVM(VM memory vm) external view returns (bool valid, string memory reason); function verifySignatures(bytes32 hash, Signature[] memory signatures, GuardianSet memory guardianSet) external pure returns (bool valid, string memory reason); function parseVM(bytes memory encodedVM) external pure returns (VM memory vm); function getGuardianSet(uint32 index) external view returns (GuardianSet memory); function getCurrentGuardianSetIndex() external view returns (uint32); function getGuardianSetExpiry() external view returns (uint32); function governanceActionIsConsumed(bytes32 hash) external view returns (bool); function isInitialized(address impl) external view returns (bool); function chainId() external view returns (uint16); function governanceChainId() external view returns (uint16); function governanceContract() external view returns (bytes32); function messageFee() external view returns (uint256); function evmChainId() external view returns (uint256); function nextSequence(address emitter) external view returns (uint64); }
// SPDX-License-Identifier: Unlicense /* * @title Solidity Bytes Arrays Utils * @author Gonçalo Sá <[email protected]> * * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. */ pragma solidity >=0.8.0 <0.9.0; library BytesLib { function concat( bytes memory _preBytes, bytes memory _postBytes ) internal pure returns (bytes memory) { bytes memory tempBytes; assembly { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // Store the length of the first bytes array at the beginning of // the memory for tempBytes. let length := mload(_preBytes) mstore(tempBytes, length) // Maintain a memory counter for the current write location in the // temp bytes array by adding the 32 bytes for the array length to // the starting location. let mc := add(tempBytes, 0x20) // Stop copying when the memory counter reaches the length of the // first bytes array. let end := add(mc, length) for { // Initialize a copy counter to the start of the _preBytes data, // 32 bytes into its memory. let cc := add(_preBytes, 0x20) } lt(mc, end) { // Increase both counters by 32 bytes each iteration. mc := add(mc, 0x20) cc := add(cc, 0x20) } { // Write the _preBytes data into the tempBytes memory 32 bytes // at a time. mstore(mc, mload(cc)) } // Add the length of _postBytes to the current length of tempBytes // and store it as the new length in the first 32 bytes of the // tempBytes memory. length := mload(_postBytes) mstore(tempBytes, add(length, mload(tempBytes))) // Move the memory counter back from a multiple of 0x20 to the // actual end of the _preBytes data. mc := end // Stop copying when the memory counter reaches the new combined // length of the arrays. end := add(mc, length) for { let cc := add(_postBytes, 0x20) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } // Update the free-memory pointer by padding our last write location // to 32 bytes: add 31 bytes to the end of tempBytes to move to the // next 32 byte block, then round down to the nearest multiple of // 32. If the sum of the length of the two arrays is zero then add // one before rounding down to leave a blank 32 bytes (the length block with 0). mstore(0x40, and( add(add(end, iszero(add(length, mload(_preBytes)))), 31), not(31) // Round down to the nearest 32 bytes. )) } return tempBytes; } function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { assembly { // Read the first 32 bytes of _preBytes storage, which is the length // of the array. (We don't need to use the offset into the slot // because arrays use the entire slot.) let fslot := sload(_preBytes.slot) // Arrays of 31 bytes or less have an even value in their slot, // while longer arrays have an odd value. The actual length is // the slot divided by two for odd values, and the lowest order // byte divided by two for even values. // If the slot is even, bitwise and the slot with 255 and divide by // two to get the length. If the slot is odd, bitwise and the slot // with -1 and divide by two. let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) let mlength := mload(_postBytes) let newlength := add(slength, mlength) // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage switch add(lt(slength, 32), lt(newlength, 32)) case 2 { // Since the new array still fits in the slot, we just need to // update the contents of the slot. // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length sstore( _preBytes.slot, // all the modifications to the slot are inside this // next block add( // we can just add to the slot contents because the // bytes we want to change are the LSBs fslot, add( mul( div( // load the bytes from memory mload(add(_postBytes, 0x20)), // zero all bytes to the right exp(0x100, sub(32, mlength)) ), // and now shift left the number of bytes to // leave space for the length in the slot exp(0x100, sub(32, newlength)) ), // increase length by the double of the memory // bytes length mul(mlength, 2) ) ) ) } case 1 { // The stored value fits in the slot, but the combined value // will exceed it. // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes.slot, add(mul(newlength, 2), 1)) // The contents of the _postBytes array start 32 bytes into // the structure. Our first read should obtain the `submod` // bytes that can fit into the unused space in the last word // of the stored array. To get this, we read 32 bytes starting // from `submod`, so the data we read overlaps with the array // contents by `submod` bytes. Masking the lowest-order // `submod` bytes allows us to add that value directly to the // stored value. let submod := sub(32, slength) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore( sc, add( and( fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 ), and(mload(mc), mask) ) ) for { mc := add(mc, 0x20) sc := add(sc, 1) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } default { // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) // Start copying to the last used word of the stored array. let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes.slot, add(mul(newlength, 2), 1)) // Copy over the first `submod` bytes of the new data as in // case 1 above. let slengthmod := mod(slength, 32) let mlengthmod := mod(mlength, 32) let submod := sub(32, slengthmod) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore(sc, add(sload(sc), and(mload(mc), mask))) for { sc := add(sc, 1) mc := add(mc, 0x20) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } } } function slice( bytes memory _bytes, uint256 _start, uint256 _length ) internal pure returns (bytes memory) { require(_length + 31 >= _length, "slice_overflow"); require(_bytes.length >= _start + _length, "slice_outOfBounds"); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) //zero out the 32 bytes slice we are about to return //we need to do it because Solidity does not garbage collect mstore(tempBytes, 0) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); address tempAddress; assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) } return tempAddress; } function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { require(_bytes.length >= _start + 1 , "toUint8_outOfBounds"); uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) { require(_bytes.length >= _start + 2, "toUint16_outOfBounds"); uint16 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x2), _start)) } return tempUint; } function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) { require(_bytes.length >= _start + 4, "toUint32_outOfBounds"); uint32 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x4), _start)) } return tempUint; } function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) { require(_bytes.length >= _start + 8, "toUint64_outOfBounds"); uint64 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x8), _start)) } return tempUint; } function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) { require(_bytes.length >= _start + 12, "toUint96_outOfBounds"); uint96 tempUint; assembly { tempUint := mload(add(add(_bytes, 0xc), _start)) } return tempUint; } function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) { require(_bytes.length >= _start + 16, "toUint128_outOfBounds"); uint128 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x10), _start)) } return tempUint; } function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) { require(_bytes.length >= _start + 32, "toUint256_outOfBounds"); uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) { require(_bytes.length >= _start + 32, "toBytes32_outOfBounds"); bytes32 tempBytes32; assembly { tempBytes32 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes32; } function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { bool success = true; assembly { let length := mload(_preBytes) // if lengths don't match the arrays are not equal switch eq(length, mload(_postBytes)) case 1 { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 let mc := add(_preBytes, 0x20) let end := add(mc, length) for { let cc := add(_postBytes, 0x20) // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) } eq(add(lt(mc, end), cb), 2) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { // if any of these checks fails then arrays are not equal if iszero(eq(mload(mc), mload(cc))) { // unsuccess: success := 0 cb := 0 } } } default { // unsuccess: success := 0 } } return success; } function equalStorage( bytes storage _preBytes, bytes memory _postBytes ) internal view returns (bool) { bool success = true; assembly { // we know _preBytes_offset is 0 let fslot := sload(_preBytes.slot) // Decode the length of the stored array like in concatStorage(). let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) let mlength := mload(_postBytes) // if lengths don't match the arrays are not equal switch eq(slength, mlength) case 1 { // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage if iszero(iszero(slength)) { switch lt(slength, 32) case 1 { // blank the last byte which is the length fslot := mul(div(fslot, 0x100), 0x100) if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) { // unsuccess: success := 0 } } default { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) let sc := keccak256(0x0, 0x20) let mc := add(_postBytes, 0x20) let end := add(mc, mlength) // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) for {} eq(add(lt(mc, end), cb), 2) { sc := add(sc, 1) mc := add(mc, 0x20) } { if iszero(eq(sload(sc), mload(mc))) { // unsuccess: success := 0 cb := 0 } } } } } default { // unsuccess: success := 0 } } return success; } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.17; import {IWormhole} from "../interfaces/IWormhole.sol"; import {ITokenBridge} from "../interfaces/ITokenBridge.sol"; import {IWETH} from "../interfaces/IWETH.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./TokenBridgeRelayerSetters.sol"; abstract contract TokenBridgeRelayerGetters is TokenBridgeRelayerSetters { function owner() public view returns (address) { return _state.owner; } function pendingOwner() public view returns (address) { return _state.pendingOwner; } function wormhole() public view returns (IWormhole) { return IWormhole(_state.wormhole); } function tokenBridge() public view returns (ITokenBridge) { return ITokenBridge(payable(_state.tokenBridge)); } function WETH() public view returns (IWETH) { return IWETH(_state.wethAddress); } function unwrapWeth() public view returns (bool) { return _state.unwrapWeth; } function chainId() public view returns (uint16) { return _state.chainId; } function getRegisteredContract(uint16 emitterChainId) public view returns (bytes32) { return _state.registeredContracts[emitterChainId]; } function swapRatePrecision() public view returns (uint256) { return _state.swapRatePrecision; } function isAcceptedToken(address token) public view returns (bool) { return _state.acceptedTokens[token]; } function getAcceptedTokensList() public view returns (address[] memory) { return _state.acceptedTokensList; } function relayerFeePrecision() public view returns (uint256) { return _state.relayerFeePrecision; } function relayerFee(uint16 chainId_) public view returns (uint256) { return _state.relayerFees[chainId_]; } function maxNativeSwapAmount(address token) public view returns (uint256) { return _state.maxNativeSwapAmount[token]; } function swapRate(address token) public view returns (uint256) { return _state.swapRates[token]; } function nativeSwapRate(address token) public view returns (uint256) { uint256 nativeSwapRate_ = swapRate(_state.wethAddress); uint256 tokenSwapRate = swapRate(token); require( nativeSwapRate_ > 0 && tokenSwapRate > 0, "swap rate not set" ); return swapRatePrecision() * nativeSwapRate_ / tokenSwapRate; } function normalizeAmount( uint256 amount, uint8 decimals ) public pure returns (uint256) { if (decimals > 8) { amount /= 10 ** (decimals - 8); } return amount; } function denormalizeAmount( uint256 amount, uint8 decimals ) public pure returns (uint256) { if (decimals > 8) { amount *= 10 ** (decimals - 8); } return amount; } function getDecimals(address token) internal view returns (uint8) { (,bytes memory queriedDecimals) = token.staticcall( abi.encodeWithSignature("decimals()") ); return abi.decode(queriedDecimals, (uint8)); } function getBalance(address token) internal view returns (uint256 balance) { // fetch the specified token balance for this contract (, bytes memory queriedBalance) = token.staticcall( abi.encodeWithSelector(IERC20.balanceOf.selector, address(this)) ); balance = abi.decode(queriedBalance, (uint256)); } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.17; import {IWormhole} from "../interfaces/IWormhole.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "./TokenBridgeRelayerGetters.sol"; abstract contract TokenBridgeRelayerGovernance is TokenBridgeRelayerGetters { event OwnershipTransfered(address indexed oldOwner, address indexed newOwner); event SwapRateUpdated(address indexed token, uint256 indexed swapRate); /** * @notice Starts the ownership transfer process of the contracts. It saves * an address in the pending owner state variable. * @param chainId_ Wormhole chain ID. * @param newOwner Address of the pending owner. */ function submitOwnershipTransferRequest( uint16 chainId_, address newOwner ) public onlyOwner onlyCurrentChain(chainId_) { require(newOwner != address(0), "newOwner cannot equal address(0)"); setPendingOwner(newOwner); } /** * @notice Cancels the ownership transfer process. * @dev Sets the pending owner state variable to the zero address. */ function cancelOwnershipTransferRequest( uint16 chainId_ ) public onlyOwner onlyCurrentChain(chainId_) { setPendingOwner(address(0)); } /** * @notice Finalizes the ownership transfer to the pending owner. * @dev It checks that the caller is the pendingOwner to validate the wallet * address. It updates the owner state variable with the pendingOwner state * variable. */ function confirmOwnershipTransferRequest() public { // cache the new owner address address newOwner = pendingOwner(); require(msg.sender == newOwner, "caller must be pendingOwner"); // cache currentOwner for Event address currentOwner = owner(); // update the owner in the contract state and reset the pending owner setOwner(newOwner); setPendingOwner(address(0)); emit OwnershipTransfered(currentOwner, newOwner); } /** * @notice Updates the unwrapWeth state variable. * @dev This variable should only be set to true for chains that * support a WETH contract. Some chains (e.g. Celo, Karura, Acala) * do not support a WETH contract, and the address is set as a placeholder * for the native asset address for swapRate lookups. * @param chainId_ Wormhole chain ID. * @param unwrapWeth_ Boolean that determines if WETH is unwrapped * when transferred back to its native blockchain. */ function updateUnwrapWethFlag( uint16 chainId_, bool unwrapWeth_ ) public onlyOwner onlyCurrentChain(chainId_) { setUnwrapWethFlag(unwrapWeth_); } /** * @notice Registers foreign Token Bridge Relayer contracts. * @param chainId_ Wormhole chain ID of the foreign contract. * @param contractAddress Address of the foreign contract in bytes32 format * (zero-left-padded address). */ function registerContract( uint16 chainId_, bytes32 contractAddress ) public onlyOwner { // sanity check both input arguments require( contractAddress != bytes32(0), "contractAddress cannot equal bytes32(0)" ); require( chainId_ != 0 && chainId_ != chainId(), "chainId_ cannot equal 0 or this chainId" ); // update the registeredContracts state variable _registerContract(chainId_, contractAddress); } /** * @notice Register tokens accepted by this contract. * @param chainId_ Wormhole chain ID. * @param token Address of the token. */ function registerToken( uint16 chainId_, address token ) public onlyOwner onlyCurrentChain(chainId_) { require(token != address(0), "invalid token"); addAcceptedToken(token); } /** * @notice Deregister tokens accepted by this contract. * @dev The `removeAcceptedToken` function will revert * if the token is not registered. * @param chainId_ Wormhole chain ID. * @param token Address of the token. */ function deregisterToken( uint16 chainId_, address token ) public onlyOwner onlyCurrentChain(chainId_) { require(token != address(0), "invalid token"); removeAcceptedToken(token); } /** * @notice Updates the fee for relaying transfers to foreign contracts. * @param chainId_ Wormhole chain ID. * @param amount Amount of USD to pay the relayer upon redemption. * @dev The relayerFee is scaled by the relayerFeePrecision. For example, * if the relayerFee is $15 and the relayerFeePrecision is 1000000, the * relayerFee should be set to 15000000. */ function updateRelayerFee( uint16 chainId_, uint256 amount ) public onlyOwner { require(chainId_ != chainId(), "invalid chain"); require( getRegisteredContract(chainId_) != bytes32(0), "contract doesn't exist" ); setRelayerFee(chainId_, amount); } /** * @notice Updates the precision of the relayer fee. * @param chainId_ Wormhole chain ID. * @param relayerFeePrecision_ Precision of relayer fee. */ function updateRelayerFeePrecision( uint16 chainId_, uint256 relayerFeePrecision_ ) public onlyOwner onlyCurrentChain(chainId_) { require(relayerFeePrecision_ > 0, "precision must be > 0"); setRelayerFeePrecision(relayerFeePrecision_); } /** * @notice Updates the swap rate for specified token in USD. * @param chainId_ Wormhole chain ID. * @param token Address of the token to update the conversion rate for. * @param swapRate The token -> USD conversion rate. * @dev The swapRate is the conversion rate using asset prices denominated in * USD multiplied by the swapRatePrecision. For example, if the conversion * rate is $15 and the swapRatePrecision is 1000000, the swapRate should be set * to 15000000. */ function updateSwapRate( uint16 chainId_, address token, uint256 swapRate ) public onlyOwner onlyCurrentChain(chainId_) { require(isAcceptedToken(token), "token not accepted"); require(swapRate > 0, "swap rate must be nonzero"); setSwapRate(token, swapRate); emit SwapRateUpdated(token, swapRate); } /** * @notice Updates the precision of the swap rate. * @param chainId_ Wormhole chain ID. * @param swapRatePrecision_ Precision of swap rate. */ function updateSwapRatePrecision( uint16 chainId_, uint256 swapRatePrecision_ ) public onlyOwner onlyCurrentChain(chainId_) { require(swapRatePrecision_ > 0, "precision must be > 0"); setSwapRatePrecision(swapRatePrecision_); } /** * @notice Updates the max amount of native assets the contract will pay * to the target recipient. * @param chainId_ Wormhole chain ID. * @param token Address of the token to update the max native swap amount for. * @param maxAmount Max amount of native assets. */ function updateMaxNativeSwapAmount( uint16 chainId_, address token, uint256 maxAmount ) public onlyOwner onlyCurrentChain(chainId_) { require(isAcceptedToken(token), "token not accepted"); setMaxNativeSwapAmount(token, maxAmount); } modifier onlyOwner() { require(owner() == msg.sender, "caller not the owner"); _; } modifier onlyCurrentChain(uint16 chainId_) { require(chainId() == chainId_, "wrong chain"); _; } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.17; import "../libraries/BytesLib.sol"; import "./TokenBridgeRelayerStructs.sol"; abstract contract TokenBridgeRelayerMessages is TokenBridgeRelayerStructs { using BytesLib for bytes; /** * @notice Encodes the TransferWithRelay struct into bytes. * @param transfer TransferWithRelay struct. * @return encoded TransferWithRelay struct encoded into bytes. */ function encodeTransferWithRelay( TransferWithRelay memory transfer ) public pure returns (bytes memory encoded) { require(transfer.payloadId == 1, "invalid payloadId"); encoded = abi.encodePacked( transfer.payloadId, transfer.targetRelayerFee, transfer.toNativeTokenAmount, transfer.targetRecipient ); } /** * @notice Decodes an encoded `TransferWithRelay` struct. * @dev reverts if: * - the first byte (payloadId) does not equal 1 * - the length of the payload has an unexpected length * @param encoded Encoded `TransferWithRelay` struct. * @return transfer `TransferTokenRelay` struct. */ function decodeTransferWithRelay( bytes memory encoded ) public pure returns (TransferWithRelay memory transfer) { uint256 index = 0; // parse the payloadId transfer.payloadId = encoded.toUint8(index); index += 1; require(transfer.payloadId == 1, "invalid payloadId"); // target relayer fee transfer.targetRelayerFee = encoded.toUint256(index); index += 32; // amount of tokens to convert to native assets transfer.toNativeTokenAmount = encoded.toUint256(index); index += 32; // recipient of the transfered tokens and native assets transfer.targetRecipient = encoded.toBytes32(index); index += 32; require(index == encoded.length, "invalid message length"); } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.17; import "./TokenBridgeRelayerState.sol"; abstract contract TokenBridgeRelayerSetters is TokenBridgeRelayerState { function setOwner(address owner_) internal { _state.owner = owner_; } function setPendingOwner(address pendingOwner_) internal { _state.pendingOwner = pendingOwner_; } function setWormhole(address wormhole_) internal { _state.wormhole = payable(wormhole_); } function setTokenBridge(address tokenBridge_) internal { _state.tokenBridge = payable(tokenBridge_); } function setUnwrapWethFlag(bool unwrapWeth_) internal { _state.unwrapWeth = unwrapWeth_; } function setWethAddress(address weth_) internal { _state.wethAddress = weth_; } function setChainId(uint16 chainId_) internal { _state.chainId = chainId_; } function _registerContract(uint16 chainId_, bytes32 contract_) internal { _state.registeredContracts[chainId_] = contract_; } function setSwapRatePrecision(uint256 precision) internal { _state.swapRatePrecision = precision; } function setRelayerFeePrecision(uint256 precision) internal { _state.relayerFeePrecision = precision; } function addAcceptedToken(address token) internal { require( _state.acceptedTokens[token] == false, "token already registered" ); _state.acceptedTokens[token] = true; _state.acceptedTokensList.push(token); } function removeAcceptedToken(address token) internal { require( _state.acceptedTokens[token], "token not registered" ); // Remove the token from the acceptedTokens mapping, and // clear the token's swapRate and maxNativeSwapAmount. _state.acceptedTokens[token] = false; _state.swapRates[token] = 0; _state.maxNativeSwapAmount[token] = 0; // cache array length uint256 length_ = _state.acceptedTokensList.length; // Replace `token` in the acceptedTokensList with the last // element in the acceptedTokensList array. uint256 i = 0; for (; i < length_;) { if (_state.acceptedTokensList[i] == token) { break; } unchecked { i += 1; } } if (i != length_) { if (length_ > 1) { _state.acceptedTokensList[i] = _state.acceptedTokensList[length_ - 1]; } _state.acceptedTokensList.pop(); } } function setRelayerFee(uint16 chainId_, uint256 fee) internal { _state.relayerFees[chainId_] = fee; } function setSwapRate(address token, uint256 swapRate) internal { _state.swapRates[token] = swapRate; } function setMaxNativeSwapAmount(address token, uint256 maximum) internal { _state.maxNativeSwapAmount[token] = maximum; } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.17; import {IWormhole} from "../interfaces/IWormhole.sol"; abstract contract TokenBridgeRelayerStorage { struct State { // Wormhole chain ID of this contract uint16 chainId; // boolean to determine if weth is unwrappable bool unwrapWeth; // address of WETH on this chain address wethAddress; // owner of this contract address owner; // intermediate state when transfering contract ownership address pendingOwner; // address of the Wormhole contract on this chain address wormhole; // address of the Wormhole TokenBridge contract on this chain address tokenBridge; // precision of the nativeSwapRates, this value should NEVER be set to zero uint256 swapRatePrecision; // precision of the relayerFee, this value should NEVER be set to zero uint256 relayerFeePrecision; // Wormhole chain ID to known relayer contract address mapping mapping(uint16 => bytes32) registeredContracts; // token swap rate in USD terms mapping(address => uint256) swapRates; /** * Mapping of source token address to maximum native asset swap amount * allowed. */ mapping(address => uint256) maxNativeSwapAmount; // mapping of chainId to relayerFee in USD mapping(uint16 => uint256) relayerFees; // accepted token to bool mapping mapping(address => bool) acceptedTokens; // list of accepted token addresses address[] acceptedTokensList; } } abstract contract TokenBridgeRelayerState { TokenBridgeRelayerStorage.State _state; }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.17; abstract contract TokenBridgeRelayerStructs { struct TransferWithRelay { uint8 payloadId; // == 1 uint256 targetRelayerFee; uint256 toNativeTokenAmount; bytes32 targetRecipient; } struct InternalTransferParams { address token; uint8 tokenDecimals; uint256 amount; uint256 toNativeTokenAmount; uint16 targetChain; bytes32 targetRecipient; } }
{ "remappings": [ "@openzeppelin/=node_modules/@openzeppelin/", "@solidity-parser/=node_modules/@solidity-parser/", "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "wormhole-solidity/=modules/src/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "bytecodeHash": "ipfs" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "london", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"uint16","name":"chainId","type":"uint16"},{"internalType":"address","name":"wormhole","type":"address"},{"internalType":"address","name":"tokenBridge_","type":"address"},{"internalType":"address","name":"wethAddress","type":"address"},{"internalType":"bool","name":"unwrapWeth_","type":"bool"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransfered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"address","name":"relayer","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nativeAmount","type":"uint256"}],"name":"SwapExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"swapRate","type":"uint256"}],"name":"SwapRateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint16","name":"emitterChainId","type":"uint16"},{"indexed":true,"internalType":"bytes32","name":"emitterAddress","type":"bytes32"},{"indexed":true,"internalType":"uint64","name":"sequence","type":"uint64"}],"name":"TransferRedeemed","type":"event"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"calculateMaxSwapAmountIn","outputs":[{"internalType":"uint256","name":"maxAllowed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"toNativeAmount","type":"uint256"}],"name":"calculateNativeSwapAmountOut","outputs":[{"internalType":"uint256","name":"nativeAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"targetChainId","type":"uint16"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"calculateRelayerFee","outputs":[{"internalType":"uint256","name":"feeInTokenDenomination","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"}],"name":"cancelOwnershipTransferRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"chainId","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedTransferMessage","type":"bytes"}],"name":"completeTransferWithRelay","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"confirmOwnershipTransferRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"encoded","type":"bytes"}],"name":"decodeTransferWithRelay","outputs":[{"components":[{"internalType":"uint8","name":"payloadId","type":"uint8"},{"internalType":"uint256","name":"targetRelayerFee","type":"uint256"},{"internalType":"uint256","name":"toNativeTokenAmount","type":"uint256"},{"internalType":"bytes32","name":"targetRecipient","type":"bytes32"}],"internalType":"struct TokenBridgeRelayerStructs.TransferWithRelay","name":"transfer","type":"tuple"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"denormalizeAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"address","name":"token","type":"address"}],"name":"deregisterToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint8","name":"payloadId","type":"uint8"},{"internalType":"uint256","name":"targetRelayerFee","type":"uint256"},{"internalType":"uint256","name":"toNativeTokenAmount","type":"uint256"},{"internalType":"bytes32","name":"targetRecipient","type":"bytes32"}],"internalType":"struct TokenBridgeRelayerStructs.TransferWithRelay","name":"transfer","type":"tuple"}],"name":"encodeTransferWithRelay","outputs":[{"internalType":"bytes","name":"encoded","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"fetchLocalAddressFromTransferMessage","outputs":[{"internalType":"address","name":"localAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAcceptedTokensList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"emitterChainId","type":"uint16"}],"name":"getRegisteredContract","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"isAcceptedToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"maxNativeSwapAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"nativeSwapRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"normalizeAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"bytes32","name":"contractAddress","type":"bytes32"}],"name":"registerContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"address","name":"token","type":"address"}],"name":"registerToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"}],"name":"relayerFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"relayerFeePrecision","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"address","name":"newOwner","type":"address"}],"name":"submitOwnershipTransferRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"swapRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swapRatePrecision","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenBridge","outputs":[{"internalType":"contract ITokenBridge","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"toNativeTokenAmount","type":"uint256"},{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"bytes32","name":"targetRecipient","type":"bytes32"},{"internalType":"uint32","name":"batchId","type":"uint32"}],"name":"transferTokensWithRelay","outputs":[{"internalType":"uint64","name":"messageSequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"unwrapWeth","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"maxAmount","type":"uint256"}],"name":"updateMaxNativeSwapAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"updateRelayerFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"uint256","name":"relayerFeePrecision_","type":"uint256"}],"name":"updateRelayerFeePrecision","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"swapRate","type":"uint256"}],"name":"updateSwapRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"uint256","name":"swapRatePrecision_","type":"uint256"}],"name":"updateSwapRatePrecision","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"bool","name":"unwrapWeth_","type":"bool"}],"name":"updateUnwrapWethFlag","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wormhole","outputs":[{"internalType":"contract IWormhole","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"toNativeTokenAmount","type":"uint256"},{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"bytes32","name":"targetRecipient","type":"bytes32"},{"internalType":"uint32","name":"batchId","type":"uint32"}],"name":"wrapAndTransferEthWithRelay","outputs":[{"internalType":"uint64","name":"messageSequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60806040523480156200001157600080fd5b50604051620040b9380380620040b9833981016040819052620000349162000260565b6001600d5561ffff8516620000825760405162461bcd60e51b815260206004820152600f60248201526e1a5b9d985b1a590818da185a5b9259608a1b60448201526064015b60405180910390fd5b6001600160a01b038416620000da5760405162461bcd60e51b815260206004820152601860248201527f696e76616c696420776f726d686f6c6520616464726573730000000000000000604482015260640162000079565b6001600160a01b038316620001325760405162461bcd60e51b815260206004820152601c60248201527f696e76616c696420746f6b656e20627269646765206164647265737300000000604482015260640162000079565b6001600160a01b0382166200018a5760405162461bcd60e51b815260206004820152601460248201527f696e76616c696420776574682061646472657373000000000000000000000000604482015260640162000079565b600180546001600160a01b031916331790556000805461ffff191661ffff8716179055600380546001600160a01b0319166001600160a01b038616179055600480546001600160a01b0319166001600160a01b038516179055600080546301000000600160b81b03191663010000006001600160a01b038516021790556000805462ff0000191662010000831515021790556200022a6305f5e100600555565b620002386305f5e100600655565b5050505050620002e0565b80516001600160a01b03811681146200025b57600080fd5b919050565b600080600080600060a086880312156200027957600080fd5b855161ffff811681146200028c57600080fd5b94506200029c6020870162000243565b9350620002ac6040870162000243565b9250620002bc6060870162000243565b915060808601518015158114620002d257600080fd5b809150509295509295909350565b613dc980620002f06000396000f3fe6080604052600436106102345760003560e01c80637c9dec7a1161012e578063a2f32c8f116100ab578063cd601c781161006f578063cd601c781461071a578063df160d761461073a578063e30c39781461075c578063f23f82ef1461077a578063fff982a81461079a57600080fd5b8063a2f32c8f14610677578063ad5c464814610697578063b5419523146106bc578063c6328a46146106dc578063c9c22f9f146106fa57600080fd5b806390a1afaa116100f257806390a1afaa146105c957806394cc743d146105e95780639a8a0592146106095780639cf278cd1461062c5780639fe089ad1461064a57600080fd5b80637c9dec7a146104f75780638335572f1461054d57806384acd1bb1461056d5780638da5cb5b1461058b5780638e151dd1146105a957600080fd5b80632def3e16116101bc5780633d62cca0116101805780633d62cca014610440578063415828bf1461047157806351e2d7b5146104865780635b9cf0e1146104a657806366775a6b146104c657600080fd5b80632def3e16146103875780632efe4f42146103a75780632f25e25f146103dd57806339ba6611146103f05780633b6e750f1461041057600080fd5b806327105ab91161020357806327105ab9146102c657806328ded8e3146102e657806329ac8361146103065780632ca8008c146103195780632d77e8fe1461034f57600080fd5b8063016aa20514610240578063038c0b66146102625780631019d654146102775780631acdab4b146102a757600080fd5b3661023b57005b600080fd5b34801561024c57600080fd5b5061026061025b3660046131a1565b6107ba565b005b34801561026e57600080fd5b50610260610884565b61028a6102853660046131ec565b610971565b6040516001600160401b0390911681526020015b60405180910390f35b3480156102b357600080fd5b506005545b60405190815260200161029e565b3480156102d257600080fd5b506102b86102e1366004613262565b610ac3565b3480156102f257600080fd5b506102b8610301366004613262565b610afd565b61028a610314366004613287565b610b2b565b34801561032557600080fd5b506102b86103343660046132d1565b6001600160a01b031660009081526008602052604090205490565b34801561035b57600080fd5b5061036f61036a3660046133c9565b610d7b565b6040516001600160a01b03909116815260200161029e565b34801561039357600080fd5b506102606103a2366004613448565b610e9b565b3480156103b357600080fd5b506102b86103c23660046132d1565b6001600160a01b031660009081526009602052604090205490565b6102606103eb366004613474565b610f59565b3480156103fc57600080fd5b506102b861040b3660046134e5565b611274565b34801561041c57600080fd5b5061043061042b3660046132d1565b611327565b604051901515815260200161029e565b34801561044c57600080fd5b506102b861045b366004613530565b61ffff1660009081526007602052604090205490565b34801561047d57600080fd5b506006546102b8565b34801561049257600080fd5b506102606104a1366004613448565b611345565b3480156104b257600080fd5b506102606104c1366004613448565b61146f565b3480156104d257600080fd5b506102b86104e1366004613530565b61ffff166000908152600a602052604090205490565b34801561050357600080fd5b506105176105123660046133c9565b611566565b60405161029e9190815160ff16815260208083015190820152604080830151908201526060918201519181019190915260800190565b34801561055957600080fd5b5061026061056836600461355b565b611697565b34801561057957600080fd5b506003546001600160a01b031661036f565b34801561059757600080fd5b506001546001600160a01b031661036f565b3480156105b557600080fd5b506102b86105c4366004613589565b61171e565b3480156105d557600080fd5b506102606105e4366004613448565b6117fe565b3480156105f557600080fd5b506102606106043660046131a1565b6118bc565b34801561061557600080fd5b5060005460405161ffff909116815260200161029e565b34801561063857600080fd5b5060005462010000900460ff16610430565b34801561065657600080fd5b5061066a6106653660046135a7565b611988565b60405161029e9190613665565b34801561068357600080fd5b50610260610692366004613530565b611a33565b3480156106a357600080fd5b50600054630100000090046001600160a01b031661036f565b3480156106c857600080fd5b506102b86106d73660046132d1565b611aaa565b3480156106e857600080fd5b506004546001600160a01b031661036f565b34801561070657600080fd5b506102606107153660046131a1565b611b44565b34801561072657600080fd5b506102b86107353660046132d1565b611c00565b34801561074657600080fd5b5061074f611ce5565b60405161029e9190613678565b34801561076857600080fd5b506002546001600160a01b031661036f565b34801561078657600080fd5b506102606107953660046136c5565b611d4a565b3480156107a657600080fd5b506102606107b53660046136c5565b611e75565b336107cd6001546001600160a01b031690565b6001600160a01b0316146107fc5760405162461bcd60e51b81526004016107f390613706565b60405180910390fd5b818061ffff1661080f60005461ffff1690565b61ffff16146108305760405162461bcd60e51b81526004016107f390613734565b6001600160a01b0382166108765760405162461bcd60e51b815260206004820152600d60248201526c34b73b30b634b2103a37b5b2b760991b60448201526064016107f3565b61087f82611f29565b505050565b60006108986002546001600160a01b031690565b9050336001600160a01b038216146108f25760405162461bcd60e51b815260206004820152601b60248201527f63616c6c6572206d7573742062652070656e64696e674f776e6572000000000060448201526064016107f3565b60006109066001546001600160a01b031690565b600180546001600160a01b0319166001600160a01b038516179055905061092d60006120ce565b816001600160a01b0316816001600160a01b03167f0d18b5fd22306e373229b9439188228edca81207d1667f604daf6cef8aa3ee6760405160405180910390a35050565b600061097b6120f0565b600061098f6003546001600160a01b031690565b6001600160a01b0316631a90a2196040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109f09190613759565b9050803414610a365760405162461bcd60e51b8152602060048201526012602482015271696e73756666696369656e742076616c756560701b60448201526064016107f3565b6000610a4189612149565b9050610a56610a508983610ac3565b82610afd565b9750610a6289896121eb565b9750610aab6040518060c001604052808b6001600160a01b031681526020018360ff1681526020018a81526020018981526020018861ffff168152602001878152508584612221565b92505050610ab96001600d55565b9695505050505050565b600060088260ff161115610af457610adc600883613788565b610ae790600a613885565b610af19084613894565b92505b50815b92915050565b600060088260ff161115610af457610b16600883613788565b610b2190600a613885565b610af190846138b6565b6000805462010000900460ff16610b845760405162461bcd60e51b815260206004820181905260248201527f574554482066756e6374696f6e616c697479206e6f7420737570706f7274656460448201526064016107f3565b6000610b986003546001600160a01b031690565b6001600160a01b0316631a90a2196040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bf99190613759565b9050803411610c3f5760405162461bcd60e51b8152602060048201526012602482015271696e73756666696369656e742076616c756560701b60448201526064016107f3565b6000610c4b82346138cd565b90506000610c64610c5d836012610ac3565b6012610afd565b610c6e90836138cd565b90508015610ca557604051339082156108fc029083906000818181858888f19350505050158015610ca3573d6000803e3d6000fd5b505b6000610cb182846138cd565b90506000610ccf6000546001600160a01b0363010000009091041690565b9050806001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b158015610d0c57600080fd5b505af1158015610d20573d6000803e3d6000fd5b5050505050610d6d6040518060c00160405280836001600160a01b03168152602001601260ff1681526020018481526020018c81526020018b61ffff1681526020018a8152508887612221565b9a9950505050505050505050565b600080610d8983602161253b565b90506000610d98846041612599565b9050610da760005461ffff1690565b61ffff168161ffff1614610e88576004546001600160a01b0316604051630ff8f14360e11b815261ffff83166004820152602481018490526001600160a01b039190911690631ff1e28690604401602060405180830381865afa158015610e12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e3691906138e0565b92506001600160a01b038316610e835760405162461bcd60e51b81526020600482015260126024820152711d1bdad95b881b9bdd08185d1d195cdd195960721b60448201526064016107f3565b610e94565b610e91826125f6565b92505b5050919050565b33610eae6001546001600160a01b031690565b6001600160a01b031614610ed45760405162461bcd60e51b81526004016107f390613706565b818061ffff16610ee760005461ffff1690565b61ffff1614610f085760405162461bcd60e51b81526004016107f390613734565b60008211610f505760405162461bcd60e51b81526020600482015260156024820152740707265636973696f6e206d757374206265203e203605c1b60448201526064016107f3565b61087f82600555565b6000806000610f9d85858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061264a92505050565b9250925092506000610fae84611566565b90506000610fbf82606001516125f6565b90506000610fd660005460ff620100009091041690565b90506001600160a01b0382163303610ffe57610ff484838784612926565b5050505050505050565b600061100985612149565b9050611019846020015182610afd565b6020850152600054630100000090046001600160a01b03166001600160a01b0316856001600160a01b031603611064576110598684866020015185612a50565b505050505050505050565b60408401511561122b5761107c846040015182610afd565b604085015260008061108d87611c00565b905080866040015111156110a357604086018190525b6110b187876040015161171e565b915081156111ed57813410156111095760405162461bcd60e51b815260206004820181905260248201527f696e73756666696369656e74206e617469766520617373657420616d6f756e7460448201526064016107f3565b600061111583346138cd565b9050801561114c57604051339082156108fc029083906000818181858888f1935050505015801561114a573d6000803e3d6000fd5b505b6040516001600160a01b0387169084156108fc029085906000818181858888f19350505050158015611182573d6000803e3d6000fd5b50876001600160a01b0316336001600160a01b0316876001600160a01b03167f764f0dc063c06f32d89a3f3af80c0db4be8a090901f589a478b447e0a51f09f18a60400151876040516111df929190918252602082015260400190565b60405180910390a450611228565b6000604087015234156112285760405133903480156108fc02916000818181858888f19350505050158015611226573d6000803e3d6000fd5b505b50505b60008460400151856020015161124191906138fd565b9050801561125457611254863383612ba8565b6112688685611263848b6138cd565b612ba8565b50505050505050505050565b6001600160a01b038216600090815260086020526040812054806000036112d15760405162461bcd60e51b81526020600482015260116024820152701cddd85c081c985d19481b9bdd081cd95d607a1b60448201526064016107f3565b6006546112de90826138b6565b60055461ffff87166000908152600a602052604090205461130086600a613885565b61130a91906138b6565b61131491906138b6565b61131e9190613894565b95945050505050565b6001600160a01b03166000908152600b602052604090205460ff1690565b336113586001546001600160a01b031690565b6001600160a01b03161461137e5760405162461bcd60e51b81526004016107f390613706565b806113db5760405162461bcd60e51b815260206004820152602760248201527f636f6e7472616374416464726573732063616e6e6f7420657175616c206279746044820152666573333228302960c81b60648201526084016107f3565b61ffff8216158015906113f7575060005461ffff838116911614155b6114535760405162461bcd60e51b815260206004820152602760248201527f636861696e49645f2063616e6e6f7420657175616c2030206f7220746869732060448201526618da185a5b925960ca1b60648201526084016107f3565b61ffff91909116600090815260076020526040902055565b5050565b336114826001546001600160a01b031690565b6001600160a01b0316146114a85760405162461bcd60e51b81526004016107f390613706565b60005461ffff1661ffff168261ffff16036114f55760405162461bcd60e51b815260206004820152600d60248201526c34b73b30b634b21031b430b4b760991b60448201526064016107f3565b61ffff821660009081526007602052604090205461154e5760405162461bcd60e51b815260206004820152601660248201527518dbdb9d1c9858dd08191bd95cdb89dd08195e1a5cdd60521b60448201526064016107f3565b61ffff919091166000908152600a6020526040902055565b6040805160808101825260008082526020820181905291810182905260608101829052906115948382612c0b565b60ff1682526115a46001826138fd565b9050816000015160ff166001146115f15760405162461bcd60e51b81526020600482015260116024820152701a5b9d985b1a59081c185e5b1bd8591259607a1b60448201526064016107f3565b6115fb8382612c67565b60208084019190915261160e90826138fd565b905061161a8382612c67565b604083015261162a6020826138fd565b9050611636838261253b565b60608301526116466020826138fd565b9050825181146116915760405162461bcd60e51b81526020600482015260166024820152750d2dcecc2d8d2c840dacae6e6c2ceca40d8cadccee8d60531b60448201526064016107f3565b50919050565b336116aa6001546001600160a01b031690565b6001600160a01b0316146116d05760405162461bcd60e51b81526004016107f390613706565b818061ffff166116e360005461ffff1690565b61ffff16146117045760405162461bcd60e51b81526004016107f390613734565b6000805462ff000019166201000084151502179055505050565b60008061172a84612149565b9050600061175061174b6000546001600160a01b0363010000009091041690565b612149565b90508060ff168260ff1611156117ad5761176a8183613788565b61177590600a613885565b61177e86611aaa565b61178891906138b6565b8461179260055490565b61179c91906138b6565b6117a69190613894565b92506117f6565b6117b685611aaa565b6117c08383613788565b6117cb90600a613885565b856117d560055490565b6117df91906138b6565b6117e991906138b6565b6117f39190613894565b92505b505092915050565b336118116001546001600160a01b031690565b6001600160a01b0316146118375760405162461bcd60e51b81526004016107f390613706565b818061ffff1661184a60005461ffff1690565b61ffff161461186b5760405162461bcd60e51b81526004016107f390613734565b600082116118b35760405162461bcd60e51b81526020600482015260156024820152740707265636973696f6e206d757374206265203e203605c1b60448201526064016107f3565b61087f82600655565b336118cf6001546001600160a01b031690565b6001600160a01b0316146118f55760405162461bcd60e51b81526004016107f390613706565b818061ffff1661190860005461ffff1690565b61ffff16146119295760405162461bcd60e51b81526004016107f390613734565b6001600160a01b03821661197f5760405162461bcd60e51b815260206004820181905260248201527f6e65774f776e65722063616e6e6f7420657175616c206164647265737328302960448201526064016107f3565b61087f826120ce565b6060816000015160ff166001146119d55760405162461bcd60e51b81526020600482015260116024820152701a5b9d985b1a59081c185e5b1bd8591259607a1b60448201526064016107f3565b815160208084015160408086015160608701519151611a1d95949192910160f89490941b6001600160f81b031916845260018401929092526021830152604182015260610190565b6040516020818303038152906040529050919050565b33611a466001546001600160a01b031690565b6001600160a01b031614611a6c5760405162461bcd60e51b81526004016107f390613706565b808061ffff16611a7f60005461ffff1690565b61ffff1614611aa05760405162461bcd60e51b81526004016107f390613734565b61146b60006120ce565b600080546001600160a01b0363010000009091048116825260086020526040808320549184168352822054600082118015611ae55750600081115b611b255760405162461bcd60e51b81526020600482015260116024820152701cddd85c081c985d19481b9bdd081cd95d607a1b60448201526064016107f3565b8082611b3060055490565b611b3a91906138b6565b610e919190613894565b33611b576001546001600160a01b031690565b6001600160a01b031614611b7d5760405162461bcd60e51b81526004016107f390613706565b818061ffff16611b9060005461ffff1690565b61ffff1614611bb15760405162461bcd60e51b81526004016107f390613734565b6001600160a01b038216611bf75760405162461bcd60e51b815260206004820152600d60248201526c34b73b30b634b2103a37b5b2b760991b60448201526064016107f3565b61087f82612cbc565b600080611c0c83612149565b90506000611c2d61174b6000546001600160a01b0363010000009091041690565b90508060ff168260ff161115611c9c57600554611c4a8284613788565b611c5590600a613885565b611c5e86611aaa565b6001600160a01b038716600090815260096020526040902054611c8191906138b6565b611c8b91906138b6565b611c959190613894565b9250610e94565b600554611ca98383613788565b611cb490600a613885565b611cbe91906138b6565b611cc785611aaa565b6001600160a01b038616600090815260096020526040902054611b30565b60606000600c01805480602002602001604051908101604052809291908181526020018280548015611d4057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611d22575b5050505050905090565b33611d5d6001546001600160a01b031690565b6001600160a01b031614611d835760405162461bcd60e51b81526004016107f390613706565b828061ffff16611d9660005461ffff1690565b61ffff1614611db75760405162461bcd60e51b81526004016107f390613734565b611dc083611327565b611ddc5760405162461bcd60e51b81526004016107f390613910565b60008211611e2c5760405162461bcd60e51b815260206004820152601960248201527f737761702072617465206d757374206265206e6f6e7a65726f0000000000000060448201526064016107f3565b6001600160a01b038316600081815260086020526040808220859055518492917fc6eb9fb936b61b402d503deeffc822f46492e15c2c8f079815cc4850ad7b02b091a350505050565b33611e886001546001600160a01b031690565b6001600160a01b031614611eae5760405162461bcd60e51b81526004016107f390613706565b828061ffff16611ec160005461ffff1690565b61ffff1614611ee25760405162461bcd60e51b81526004016107f390613734565b611eeb83611327565b611f075760405162461bcd60e51b81526004016107f390613910565b6001600160a01b03831660009081526009602052604090208290555b50505050565b6001600160a01b0381166000908152600b602052604090205460ff16611f885760405162461bcd60e51b81526020600482015260146024820152731d1bdad95b881b9bdd081c9959da5cdd195c995960621b60448201526064016107f3565b6001600160a01b0381166000908152600b60209081526040808320805460ff191690556008825280832083905560099091528120819055600c54905b8181101561200e57826001600160a01b03166000600c018281548110611fec57611fec61393c565b6000918252602090912001546001600160a01b03161461200e57600101611fc4565b81811461087f57600182111561209657600c61202b6001846138cd565b8154811061203b5761203b61393c565b600091825260209091200154600c80546001600160a01b0390921691839081106120675761206761393c565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b600c8054806120a7576120a7613952565b600082815260209020810160001990810180546001600160a01b0319169055019055505050565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b6002600d54036121425760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016107f3565b6002600d55565b60408051600481526024810182526020810180516001600160e01b031663313ce56760e01b179052905160009182916001600160a01b0385169161218c91613968565b600060405180830381855afa9150503d80600081146121c7576040519150601f19603f3d011682016040523d82523d6000602084013e6121cc565b606091505b50915050808060200190518101906121e49190613994565b9392505050565b6000806121f784612d8b565b905061220584333086612e37565b8061220f85612d8b565b61221991906138cd565b949350505050565b60006122308460000151611327565b61224c5760405162461bcd60e51b81526004016107f390613910565b60a08401516122a95760405162461bcd60e51b8152602060048201526024808201527f746172676574526563697069656e742063616e6e6f74206265206279746573336044820152633228302960e01b60648201526084016107f3565b60006122bd85604001518660200151610ac3565b90506000811161230f5760405162461bcd60e51b815260206004820152601d60248201527f6e6f726d616c697a656420616d6f756e74206d757374206265203e203000000060448201526064016107f3565b600061232386606001518760200151610ac3565b90508560600151600014806123385750600081115b6123845760405162461bcd60e51b815260206004820152601b60248201527f696e76616c696420746f4e6174697665546f6b656e416d6f756e74000000000060448201526064016107f3565b608086015161ffff16600090815260076020526040902054806123e15760405162461bcd60e51b81526020600482015260156024820152741d185c99d95d081b9bdd081c9959da5cdd195c9959605a1b60448201526064016107f3565b60006124076123fd89608001518a600001518b60200151611274565b8960200151610ac3565b905061241383826138fd565b84116124575760405162461bcd60e51b81526020600482015260136024820152721a5b9cdd59999a58da595b9d08185b5bdd5b9d606a1b60448201526064016107f3565b600061248a6040518060800160405280600160ff1681526020018481526020018681526020018b60a00151815250611988565b905060006124a06004546001600160a01b031690565b90506124b58a60000151828c60400151612e6f565b806001600160a01b031663c5a5ebda898c600001518d604001518e60800151898f896040518863ffffffff1660e01b81526004016124f8969594939291906139b1565b60206040518083038185885af1158015612516573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610d6d9190613a17565b60006125488260206138fd565b835110156125905760405162461bcd60e51b8152602060048201526015602482015274746f427974657333325f6f75744f66426f756e647360581b60448201526064016107f3565b50016020015190565b60006125a68260026138fd565b835110156125ed5760405162461bcd60e51b8152602060048201526014602482015273746f55696e7431365f6f75744f66426f756e647360601b60448201526064016107f3565b50016002015190565b60006001600160a01b03198216156126465760405162461bcd60e51b8152602060048201526013602482015272696e76616c69642045564d206164647265737360681b60448201526064016107f3565b5090565b606060008060006126636003546001600160a01b031690565b6001600160a01b031663a9e11893866040518263ffffffff1660e01b815260040161268e9190613665565b600060405180830381865afa1580156126ab573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526126d39190810190613b56565b905060006126e48260e00151610d7b565b90506126ef81611327565b6127325760405162461bcd60e51b81526020600482015260146024820152731d1bdad95b881b9bdd081c9959da5cdd195c995960621b60448201526064016107f3565b600061273d82612d8b565b905060006127536004546001600160a01b031690565b90506000816001600160a01b031663c3f511c18a6040518263ffffffff1660e01b81526004016127839190613665565b6000604051808303816000875af11580156127a2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526127ca9190810190613c7a565b90506000836127d886612d8b565b6127e291906138cd565b90506000836001600160a01b031663ea63738d846040518263ffffffff1660e01b81526004016128129190613665565b600060405180830381865afa15801561282f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526128579190810190613cae565b9050612877876060015161ffff1660009081526007602052604090205490565b8160c00151146128c95760405162461bcd60e51b815260206004820152601760248201527f636f6e7472616374206e6f74207265676973746572656400000000000000000060448201526064016107f3565b8660a001516001600160401b03168760800151886060015161ffff167fcaf280c8cfeba144da67230d9b009c8f868a75bac9a528fa0474be1ba317c16960405160405180910390a460e001519a9099509397509295505050505050565b34156129805760405162461bcd60e51b815260206004820152602360248201527f726563697069656e742063616e6e6f742073776170206e61746976652061737360448201526265747360e81b60648201526084016107f3565b6000546001600160a01b0363010000009091048116908516811480156129a35750815b15612a3e57604051632e1a7d4d60e01b8152600481018490526001600160a01b03821690632e1a7d4d90602401600060405180830381600087803b1580156129ea57600080fd5b505af11580156129fe573d6000803e3d6000fd5b50506040516001600160a01b038716925085156108fc02915085906000818181858888f19350505050158015612a38573d6000803e3d6000fd5b50612a49565b612a49858585612ba8565b5050505050565b3415612a935760405162461bcd60e51b815260206004820152601260248201527176616c7565206d757374206265207a65726f60701b60448201526064016107f3565b8015612b7957600054604051632e1a7d4d60e01b81526004810186905263010000009091046001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015612ae557600080fd5b505af1158015612af9573d6000803e3d6000fd5b50505050826001600160a01b03166108fc8386612b1691906138cd565b6040518115909202916000818181858888f19350505050158015612b3e573d6000803e3d6000fd5b508115612b7457604051339083156108fc029084906000818181858888f19350505050158015612b72573d6000803e3d6000fd5b505b611f23565b600054630100000090046001600160a01b0316612b9b818561126386896138cd565b8215612a4957612a498133855b6040516001600160a01b03831660248201526044810182905261087f90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612f84565b6000612c188260016138fd565b83511015612c5e5760405162461bcd60e51b8152602060048201526013602482015272746f55696e74385f6f75744f66426f756e647360681b60448201526064016107f3565b50016001015190565b6000612c748260206138fd565b835110156125905760405162461bcd60e51b8152602060048201526015602482015274746f55696e743235365f6f75744f66426f756e647360581b60448201526064016107f3565b6001600160a01b0381166000908152600b602052604090205460ff1615612d255760405162461bcd60e51b815260206004820152601860248201527f746f6b656e20616c72656164792072656769737465726564000000000000000060448201526064016107f3565b6001600160a01b03166000818152600b60205260408120805460ff19166001908117909155600c805491820181559091527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c70180546001600160a01b0319169091179055565b604080513060248083019190915282518083039091018152604490910182526020810180516001600160e01b03166370a0823160e01b179052905160009182916001600160a01b03851691612ddf91613968565b600060405180830381855afa9150503d8060008114612e1a576040519150601f19603f3d011682016040523d82523d6000602084013e612e1f565b606091505b50915050808060200190518101906121e49190613759565b6040516001600160a01b0380851660248301528316604482015260648101829052611f239085906323b872dd60e01b90608401612bd4565b801580612ee95750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015612ec3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ee79190613759565b155b612f545760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b60648201526084016107f3565b6040516001600160a01b03831660248201526044810182905261087f90849063095ea7b360e01b90606401612bd4565b6000612fd9826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166130569092919063ffffffff16565b80519091501561087f5780806020019051810190612ff79190613d76565b61087f5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016107f3565b6060612219848460008585600080866001600160a01b0316858760405161307d9190613968565b60006040518083038185875af1925050503d80600081146130ba576040519150601f19603f3d011682016040523d82523d6000602084013e6130bf565b606091505b50915091506130d0878383876130db565b979650505050505050565b6060831561314a578251600003613143576001600160a01b0385163b6131435760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016107f3565b5081612219565b612219838381511561315f5781518083602001fd5b8060405162461bcd60e51b81526004016107f39190613665565b61ffff8116811461318957600080fd5b50565b6001600160a01b038116811461318957600080fd5b600080604083850312156131b457600080fd5b82356131bf81613179565b915060208301356131cf8161318c565b809150509250929050565b63ffffffff8116811461318957600080fd5b60008060008060008060c0878903121561320557600080fd5b86356132108161318c565b95506020870135945060408701359350606087013561322e81613179565b92506080870135915060a0870135613245816131da565b809150509295509295509295565b60ff8116811461318957600080fd5b6000806040838503121561327557600080fd5b8235915060208301356131cf81613253565b6000806000806080858703121561329d57600080fd5b8435935060208501356132af81613179565b92506040850135915060608501356132c6816131da565b939692955090935050565b6000602082840312156132e357600080fd5b81356121e48161318c565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b0381118282101715613326576133266132ee565b60405290565b60405161016081016001600160401b0381118282101715613326576133266132ee565b60405161010081016001600160401b0381118282101715613326576133266132ee565b604051601f8201601f191681016001600160401b038111828210171561339a5761339a6132ee565b604052919050565b60006001600160401b038211156133bb576133bb6132ee565b50601f01601f191660200190565b6000602082840312156133db57600080fd5b81356001600160401b038111156133f157600080fd5b8201601f8101841361340257600080fd5b8035613415613410826133a2565b613372565b81815285602083850101111561342a57600080fd5b81602084016020830137600091810160200191909152949350505050565b6000806040838503121561345b57600080fd5b823561346681613179565b946020939093013593505050565b6000806020838503121561348757600080fd5b82356001600160401b038082111561349e57600080fd5b818501915085601f8301126134b257600080fd5b8135818111156134c157600080fd5b8660208285010111156134d357600080fd5b60209290920196919550909350505050565b6000806000606084860312156134fa57600080fd5b833561350581613179565b925060208401356135158161318c565b9150604084013561352581613253565b809150509250925092565b60006020828403121561354257600080fd5b81356121e481613179565b801515811461318957600080fd5b6000806040838503121561356e57600080fd5b823561357981613179565b915060208301356131cf8161354d565b6000806040838503121561359c57600080fd5b82356134668161318c565b6000608082840312156135b957600080fd5b604051608081018181106001600160401b03821117156135db576135db6132ee565b60405282356135e981613253565b808252506020830135602082015260408301356040820152606083013560608201528091505092915050565b60005b83811015613630578181015183820152602001613618565b50506000910152565b60008151808452613651816020860160208601613615565b601f01601f19169290920160200192915050565b6020815260006121e46020830184613639565b6020808252825182820181905260009190848201906040850190845b818110156136b95783516001600160a01b031683529284019291840191600101613694565b50909695505050505050565b6000806000606084860312156136da57600080fd5b83356136e581613179565b925060208401356136f58161318c565b929592945050506040919091013590565b60208082526014908201527331b0b63632b9103737ba103a34329037bbb732b960611b604082015260600190565b6020808252600b908201526a3bb937b7339031b430b4b760a91b604082015260600190565b60006020828403121561376b57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b60ff8281168282160390811115610af757610af7613772565b600181815b808511156137dc5781600019048211156137c2576137c2613772565b808516156137cf57918102915b93841c93908002906137a6565b509250929050565b6000826137f357506001610af7565b8161380057506000610af7565b816001811461381657600281146138205761383c565b6001915050610af7565b60ff84111561383157613831613772565b50506001821b610af7565b5060208310610133831016604e8410600b841016171561385f575081810a610af7565b61386983836137a1565b806000190482111561387d5761387d613772565b029392505050565b60006121e460ff8416836137e4565b6000826138b157634e487b7160e01b600052601260045260246000fd5b500490565b8082028115828204841417610af757610af7613772565b81810381811115610af757610af7613772565b6000602082840312156138f257600080fd5b81516121e48161318c565b80820180821115610af757610af7613772565b6020808252601290820152711d1bdad95b881b9bdd081858d8d95c1d195960721b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052603160045260246000fd5b6000825161397a818460208701613615565b9190910192915050565b805161398f81613253565b919050565b6000602082840312156139a657600080fd5b81516121e481613253565b60018060a01b038716815285602082015261ffff8516604082015283606082015263ffffffff8316608082015260c060a082015260006139f460c0830184613639565b98975050505050505050565b80516001600160401b038116811461398f57600080fd5b600060208284031215613a2957600080fd5b6121e482613a00565b805161398f816131da565b805161398f81613179565b600082601f830112613a5957600080fd5b8151613a67613410826133a2565b818152846020838601011115613a7c57600080fd5b612219826020830160208701613615565b600082601f830112613a9e57600080fd5b815160206001600160401b03821115613ab957613ab96132ee565b613ac7818360051b01613372565b82815260079290921b84018101918181019086841115613ae657600080fd5b8286015b84811015613b4b5760808189031215613b035760008081fd5b613b0b613304565b815181528482015185820152604080830151613b2681613253565b90820152606082810151613b3981613253565b90820152835291830191608001613aea565b509695505050505050565b600060208284031215613b6857600080fd5b81516001600160401b0380821115613b7f57600080fd5b908301906101608286031215613b9457600080fd5b613b9c61332c565b613ba583613984565b8152613bb360208401613a32565b6020820152613bc460408401613a32565b6040820152613bd560608401613a3d565b606082015260808301516080820152613bf060a08401613a00565b60a0820152613c0160c08401613984565b60c082015260e083015182811115613c1857600080fd5b613c2487828601613a48565b60e083015250610100613c38818501613a32565b908201526101208381015183811115613c5057600080fd5b613c5c88828701613a8d565b91830191909152506101409283015192810192909252509392505050565b600060208284031215613c8c57600080fd5b81516001600160401b03811115613ca257600080fd5b61221984828501613a48565b600060208284031215613cc057600080fd5b81516001600160401b0380821115613cd757600080fd5b908301906101008286031215613cec57600080fd5b613cf461334f565b613cfd83613984565b81526020830151602082015260408301516040820152613d1f60608401613a3d565b606082015260808301516080820152613d3a60a08401613a3d565b60a082015260c083015160c082015260e083015182811115613d5b57600080fd5b613d6787828601613a48565b60e08301525095945050505050565b600060208284031215613d8857600080fd5b81516121e48161354d56fea2646970667358221220043ecf0d1429bfc7866a5607e98ae2b1cca0eeef46c7279047c0211efe0f076764736f6c63430008110033000000000000000000000000000000000000000000000000000000000000000200000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b0000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000001
Deployed Bytecode
0x6080604052600436106102345760003560e01c80637c9dec7a1161012e578063a2f32c8f116100ab578063cd601c781161006f578063cd601c781461071a578063df160d761461073a578063e30c39781461075c578063f23f82ef1461077a578063fff982a81461079a57600080fd5b8063a2f32c8f14610677578063ad5c464814610697578063b5419523146106bc578063c6328a46146106dc578063c9c22f9f146106fa57600080fd5b806390a1afaa116100f257806390a1afaa146105c957806394cc743d146105e95780639a8a0592146106095780639cf278cd1461062c5780639fe089ad1461064a57600080fd5b80637c9dec7a146104f75780638335572f1461054d57806384acd1bb1461056d5780638da5cb5b1461058b5780638e151dd1146105a957600080fd5b80632def3e16116101bc5780633d62cca0116101805780633d62cca014610440578063415828bf1461047157806351e2d7b5146104865780635b9cf0e1146104a657806366775a6b146104c657600080fd5b80632def3e16146103875780632efe4f42146103a75780632f25e25f146103dd57806339ba6611146103f05780633b6e750f1461041057600080fd5b806327105ab91161020357806327105ab9146102c657806328ded8e3146102e657806329ac8361146103065780632ca8008c146103195780632d77e8fe1461034f57600080fd5b8063016aa20514610240578063038c0b66146102625780631019d654146102775780631acdab4b146102a757600080fd5b3661023b57005b600080fd5b34801561024c57600080fd5b5061026061025b3660046131a1565b6107ba565b005b34801561026e57600080fd5b50610260610884565b61028a6102853660046131ec565b610971565b6040516001600160401b0390911681526020015b60405180910390f35b3480156102b357600080fd5b506005545b60405190815260200161029e565b3480156102d257600080fd5b506102b86102e1366004613262565b610ac3565b3480156102f257600080fd5b506102b8610301366004613262565b610afd565b61028a610314366004613287565b610b2b565b34801561032557600080fd5b506102b86103343660046132d1565b6001600160a01b031660009081526008602052604090205490565b34801561035b57600080fd5b5061036f61036a3660046133c9565b610d7b565b6040516001600160a01b03909116815260200161029e565b34801561039357600080fd5b506102606103a2366004613448565b610e9b565b3480156103b357600080fd5b506102b86103c23660046132d1565b6001600160a01b031660009081526009602052604090205490565b6102606103eb366004613474565b610f59565b3480156103fc57600080fd5b506102b861040b3660046134e5565b611274565b34801561041c57600080fd5b5061043061042b3660046132d1565b611327565b604051901515815260200161029e565b34801561044c57600080fd5b506102b861045b366004613530565b61ffff1660009081526007602052604090205490565b34801561047d57600080fd5b506006546102b8565b34801561049257600080fd5b506102606104a1366004613448565b611345565b3480156104b257600080fd5b506102606104c1366004613448565b61146f565b3480156104d257600080fd5b506102b86104e1366004613530565b61ffff166000908152600a602052604090205490565b34801561050357600080fd5b506105176105123660046133c9565b611566565b60405161029e9190815160ff16815260208083015190820152604080830151908201526060918201519181019190915260800190565b34801561055957600080fd5b5061026061056836600461355b565b611697565b34801561057957600080fd5b506003546001600160a01b031661036f565b34801561059757600080fd5b506001546001600160a01b031661036f565b3480156105b557600080fd5b506102b86105c4366004613589565b61171e565b3480156105d557600080fd5b506102606105e4366004613448565b6117fe565b3480156105f557600080fd5b506102606106043660046131a1565b6118bc565b34801561061557600080fd5b5060005460405161ffff909116815260200161029e565b34801561063857600080fd5b5060005462010000900460ff16610430565b34801561065657600080fd5b5061066a6106653660046135a7565b611988565b60405161029e9190613665565b34801561068357600080fd5b50610260610692366004613530565b611a33565b3480156106a357600080fd5b50600054630100000090046001600160a01b031661036f565b3480156106c857600080fd5b506102b86106d73660046132d1565b611aaa565b3480156106e857600080fd5b506004546001600160a01b031661036f565b34801561070657600080fd5b506102606107153660046131a1565b611b44565b34801561072657600080fd5b506102b86107353660046132d1565b611c00565b34801561074657600080fd5b5061074f611ce5565b60405161029e9190613678565b34801561076857600080fd5b506002546001600160a01b031661036f565b34801561078657600080fd5b506102606107953660046136c5565b611d4a565b3480156107a657600080fd5b506102606107b53660046136c5565b611e75565b336107cd6001546001600160a01b031690565b6001600160a01b0316146107fc5760405162461bcd60e51b81526004016107f390613706565b60405180910390fd5b818061ffff1661080f60005461ffff1690565b61ffff16146108305760405162461bcd60e51b81526004016107f390613734565b6001600160a01b0382166108765760405162461bcd60e51b815260206004820152600d60248201526c34b73b30b634b2103a37b5b2b760991b60448201526064016107f3565b61087f82611f29565b505050565b60006108986002546001600160a01b031690565b9050336001600160a01b038216146108f25760405162461bcd60e51b815260206004820152601b60248201527f63616c6c6572206d7573742062652070656e64696e674f776e6572000000000060448201526064016107f3565b60006109066001546001600160a01b031690565b600180546001600160a01b0319166001600160a01b038516179055905061092d60006120ce565b816001600160a01b0316816001600160a01b03167f0d18b5fd22306e373229b9439188228edca81207d1667f604daf6cef8aa3ee6760405160405180910390a35050565b600061097b6120f0565b600061098f6003546001600160a01b031690565b6001600160a01b0316631a90a2196040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109f09190613759565b9050803414610a365760405162461bcd60e51b8152602060048201526012602482015271696e73756666696369656e742076616c756560701b60448201526064016107f3565b6000610a4189612149565b9050610a56610a508983610ac3565b82610afd565b9750610a6289896121eb565b9750610aab6040518060c001604052808b6001600160a01b031681526020018360ff1681526020018a81526020018981526020018861ffff168152602001878152508584612221565b92505050610ab96001600d55565b9695505050505050565b600060088260ff161115610af457610adc600883613788565b610ae790600a613885565b610af19084613894565b92505b50815b92915050565b600060088260ff161115610af457610b16600883613788565b610b2190600a613885565b610af190846138b6565b6000805462010000900460ff16610b845760405162461bcd60e51b815260206004820181905260248201527f574554482066756e6374696f6e616c697479206e6f7420737570706f7274656460448201526064016107f3565b6000610b986003546001600160a01b031690565b6001600160a01b0316631a90a2196040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bf99190613759565b9050803411610c3f5760405162461bcd60e51b8152602060048201526012602482015271696e73756666696369656e742076616c756560701b60448201526064016107f3565b6000610c4b82346138cd565b90506000610c64610c5d836012610ac3565b6012610afd565b610c6e90836138cd565b90508015610ca557604051339082156108fc029083906000818181858888f19350505050158015610ca3573d6000803e3d6000fd5b505b6000610cb182846138cd565b90506000610ccf6000546001600160a01b0363010000009091041690565b9050806001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b158015610d0c57600080fd5b505af1158015610d20573d6000803e3d6000fd5b5050505050610d6d6040518060c00160405280836001600160a01b03168152602001601260ff1681526020018481526020018c81526020018b61ffff1681526020018a8152508887612221565b9a9950505050505050505050565b600080610d8983602161253b565b90506000610d98846041612599565b9050610da760005461ffff1690565b61ffff168161ffff1614610e88576004546001600160a01b0316604051630ff8f14360e11b815261ffff83166004820152602481018490526001600160a01b039190911690631ff1e28690604401602060405180830381865afa158015610e12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e3691906138e0565b92506001600160a01b038316610e835760405162461bcd60e51b81526020600482015260126024820152711d1bdad95b881b9bdd08185d1d195cdd195960721b60448201526064016107f3565b610e94565b610e91826125f6565b92505b5050919050565b33610eae6001546001600160a01b031690565b6001600160a01b031614610ed45760405162461bcd60e51b81526004016107f390613706565b818061ffff16610ee760005461ffff1690565b61ffff1614610f085760405162461bcd60e51b81526004016107f390613734565b60008211610f505760405162461bcd60e51b81526020600482015260156024820152740707265636973696f6e206d757374206265203e203605c1b60448201526064016107f3565b61087f82600555565b6000806000610f9d85858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061264a92505050565b9250925092506000610fae84611566565b90506000610fbf82606001516125f6565b90506000610fd660005460ff620100009091041690565b90506001600160a01b0382163303610ffe57610ff484838784612926565b5050505050505050565b600061100985612149565b9050611019846020015182610afd565b6020850152600054630100000090046001600160a01b03166001600160a01b0316856001600160a01b031603611064576110598684866020015185612a50565b505050505050505050565b60408401511561122b5761107c846040015182610afd565b604085015260008061108d87611c00565b905080866040015111156110a357604086018190525b6110b187876040015161171e565b915081156111ed57813410156111095760405162461bcd60e51b815260206004820181905260248201527f696e73756666696369656e74206e617469766520617373657420616d6f756e7460448201526064016107f3565b600061111583346138cd565b9050801561114c57604051339082156108fc029083906000818181858888f1935050505015801561114a573d6000803e3d6000fd5b505b6040516001600160a01b0387169084156108fc029085906000818181858888f19350505050158015611182573d6000803e3d6000fd5b50876001600160a01b0316336001600160a01b0316876001600160a01b03167f764f0dc063c06f32d89a3f3af80c0db4be8a090901f589a478b447e0a51f09f18a60400151876040516111df929190918252602082015260400190565b60405180910390a450611228565b6000604087015234156112285760405133903480156108fc02916000818181858888f19350505050158015611226573d6000803e3d6000fd5b505b50505b60008460400151856020015161124191906138fd565b9050801561125457611254863383612ba8565b6112688685611263848b6138cd565b612ba8565b50505050505050505050565b6001600160a01b038216600090815260086020526040812054806000036112d15760405162461bcd60e51b81526020600482015260116024820152701cddd85c081c985d19481b9bdd081cd95d607a1b60448201526064016107f3565b6006546112de90826138b6565b60055461ffff87166000908152600a602052604090205461130086600a613885565b61130a91906138b6565b61131491906138b6565b61131e9190613894565b95945050505050565b6001600160a01b03166000908152600b602052604090205460ff1690565b336113586001546001600160a01b031690565b6001600160a01b03161461137e5760405162461bcd60e51b81526004016107f390613706565b806113db5760405162461bcd60e51b815260206004820152602760248201527f636f6e7472616374416464726573732063616e6e6f7420657175616c206279746044820152666573333228302960c81b60648201526084016107f3565b61ffff8216158015906113f7575060005461ffff838116911614155b6114535760405162461bcd60e51b815260206004820152602760248201527f636861696e49645f2063616e6e6f7420657175616c2030206f7220746869732060448201526618da185a5b925960ca1b60648201526084016107f3565b61ffff91909116600090815260076020526040902055565b5050565b336114826001546001600160a01b031690565b6001600160a01b0316146114a85760405162461bcd60e51b81526004016107f390613706565b60005461ffff1661ffff168261ffff16036114f55760405162461bcd60e51b815260206004820152600d60248201526c34b73b30b634b21031b430b4b760991b60448201526064016107f3565b61ffff821660009081526007602052604090205461154e5760405162461bcd60e51b815260206004820152601660248201527518dbdb9d1c9858dd08191bd95cdb89dd08195e1a5cdd60521b60448201526064016107f3565b61ffff919091166000908152600a6020526040902055565b6040805160808101825260008082526020820181905291810182905260608101829052906115948382612c0b565b60ff1682526115a46001826138fd565b9050816000015160ff166001146115f15760405162461bcd60e51b81526020600482015260116024820152701a5b9d985b1a59081c185e5b1bd8591259607a1b60448201526064016107f3565b6115fb8382612c67565b60208084019190915261160e90826138fd565b905061161a8382612c67565b604083015261162a6020826138fd565b9050611636838261253b565b60608301526116466020826138fd565b9050825181146116915760405162461bcd60e51b81526020600482015260166024820152750d2dcecc2d8d2c840dacae6e6c2ceca40d8cadccee8d60531b60448201526064016107f3565b50919050565b336116aa6001546001600160a01b031690565b6001600160a01b0316146116d05760405162461bcd60e51b81526004016107f390613706565b818061ffff166116e360005461ffff1690565b61ffff16146117045760405162461bcd60e51b81526004016107f390613734565b6000805462ff000019166201000084151502179055505050565b60008061172a84612149565b9050600061175061174b6000546001600160a01b0363010000009091041690565b612149565b90508060ff168260ff1611156117ad5761176a8183613788565b61177590600a613885565b61177e86611aaa565b61178891906138b6565b8461179260055490565b61179c91906138b6565b6117a69190613894565b92506117f6565b6117b685611aaa565b6117c08383613788565b6117cb90600a613885565b856117d560055490565b6117df91906138b6565b6117e991906138b6565b6117f39190613894565b92505b505092915050565b336118116001546001600160a01b031690565b6001600160a01b0316146118375760405162461bcd60e51b81526004016107f390613706565b818061ffff1661184a60005461ffff1690565b61ffff161461186b5760405162461bcd60e51b81526004016107f390613734565b600082116118b35760405162461bcd60e51b81526020600482015260156024820152740707265636973696f6e206d757374206265203e203605c1b60448201526064016107f3565b61087f82600655565b336118cf6001546001600160a01b031690565b6001600160a01b0316146118f55760405162461bcd60e51b81526004016107f390613706565b818061ffff1661190860005461ffff1690565b61ffff16146119295760405162461bcd60e51b81526004016107f390613734565b6001600160a01b03821661197f5760405162461bcd60e51b815260206004820181905260248201527f6e65774f776e65722063616e6e6f7420657175616c206164647265737328302960448201526064016107f3565b61087f826120ce565b6060816000015160ff166001146119d55760405162461bcd60e51b81526020600482015260116024820152701a5b9d985b1a59081c185e5b1bd8591259607a1b60448201526064016107f3565b815160208084015160408086015160608701519151611a1d95949192910160f89490941b6001600160f81b031916845260018401929092526021830152604182015260610190565b6040516020818303038152906040529050919050565b33611a466001546001600160a01b031690565b6001600160a01b031614611a6c5760405162461bcd60e51b81526004016107f390613706565b808061ffff16611a7f60005461ffff1690565b61ffff1614611aa05760405162461bcd60e51b81526004016107f390613734565b61146b60006120ce565b600080546001600160a01b0363010000009091048116825260086020526040808320549184168352822054600082118015611ae55750600081115b611b255760405162461bcd60e51b81526020600482015260116024820152701cddd85c081c985d19481b9bdd081cd95d607a1b60448201526064016107f3565b8082611b3060055490565b611b3a91906138b6565b610e919190613894565b33611b576001546001600160a01b031690565b6001600160a01b031614611b7d5760405162461bcd60e51b81526004016107f390613706565b818061ffff16611b9060005461ffff1690565b61ffff1614611bb15760405162461bcd60e51b81526004016107f390613734565b6001600160a01b038216611bf75760405162461bcd60e51b815260206004820152600d60248201526c34b73b30b634b2103a37b5b2b760991b60448201526064016107f3565b61087f82612cbc565b600080611c0c83612149565b90506000611c2d61174b6000546001600160a01b0363010000009091041690565b90508060ff168260ff161115611c9c57600554611c4a8284613788565b611c5590600a613885565b611c5e86611aaa565b6001600160a01b038716600090815260096020526040902054611c8191906138b6565b611c8b91906138b6565b611c959190613894565b9250610e94565b600554611ca98383613788565b611cb490600a613885565b611cbe91906138b6565b611cc785611aaa565b6001600160a01b038616600090815260096020526040902054611b30565b60606000600c01805480602002602001604051908101604052809291908181526020018280548015611d4057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611d22575b5050505050905090565b33611d5d6001546001600160a01b031690565b6001600160a01b031614611d835760405162461bcd60e51b81526004016107f390613706565b828061ffff16611d9660005461ffff1690565b61ffff1614611db75760405162461bcd60e51b81526004016107f390613734565b611dc083611327565b611ddc5760405162461bcd60e51b81526004016107f390613910565b60008211611e2c5760405162461bcd60e51b815260206004820152601960248201527f737761702072617465206d757374206265206e6f6e7a65726f0000000000000060448201526064016107f3565b6001600160a01b038316600081815260086020526040808220859055518492917fc6eb9fb936b61b402d503deeffc822f46492e15c2c8f079815cc4850ad7b02b091a350505050565b33611e886001546001600160a01b031690565b6001600160a01b031614611eae5760405162461bcd60e51b81526004016107f390613706565b828061ffff16611ec160005461ffff1690565b61ffff1614611ee25760405162461bcd60e51b81526004016107f390613734565b611eeb83611327565b611f075760405162461bcd60e51b81526004016107f390613910565b6001600160a01b03831660009081526009602052604090208290555b50505050565b6001600160a01b0381166000908152600b602052604090205460ff16611f885760405162461bcd60e51b81526020600482015260146024820152731d1bdad95b881b9bdd081c9959da5cdd195c995960621b60448201526064016107f3565b6001600160a01b0381166000908152600b60209081526040808320805460ff191690556008825280832083905560099091528120819055600c54905b8181101561200e57826001600160a01b03166000600c018281548110611fec57611fec61393c565b6000918252602090912001546001600160a01b03161461200e57600101611fc4565b81811461087f57600182111561209657600c61202b6001846138cd565b8154811061203b5761203b61393c565b600091825260209091200154600c80546001600160a01b0390921691839081106120675761206761393c565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b600c8054806120a7576120a7613952565b600082815260209020810160001990810180546001600160a01b0319169055019055505050565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b6002600d54036121425760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016107f3565b6002600d55565b60408051600481526024810182526020810180516001600160e01b031663313ce56760e01b179052905160009182916001600160a01b0385169161218c91613968565b600060405180830381855afa9150503d80600081146121c7576040519150601f19603f3d011682016040523d82523d6000602084013e6121cc565b606091505b50915050808060200190518101906121e49190613994565b9392505050565b6000806121f784612d8b565b905061220584333086612e37565b8061220f85612d8b565b61221991906138cd565b949350505050565b60006122308460000151611327565b61224c5760405162461bcd60e51b81526004016107f390613910565b60a08401516122a95760405162461bcd60e51b8152602060048201526024808201527f746172676574526563697069656e742063616e6e6f74206265206279746573336044820152633228302960e01b60648201526084016107f3565b60006122bd85604001518660200151610ac3565b90506000811161230f5760405162461bcd60e51b815260206004820152601d60248201527f6e6f726d616c697a656420616d6f756e74206d757374206265203e203000000060448201526064016107f3565b600061232386606001518760200151610ac3565b90508560600151600014806123385750600081115b6123845760405162461bcd60e51b815260206004820152601b60248201527f696e76616c696420746f4e6174697665546f6b656e416d6f756e74000000000060448201526064016107f3565b608086015161ffff16600090815260076020526040902054806123e15760405162461bcd60e51b81526020600482015260156024820152741d185c99d95d081b9bdd081c9959da5cdd195c9959605a1b60448201526064016107f3565b60006124076123fd89608001518a600001518b60200151611274565b8960200151610ac3565b905061241383826138fd565b84116124575760405162461bcd60e51b81526020600482015260136024820152721a5b9cdd59999a58da595b9d08185b5bdd5b9d606a1b60448201526064016107f3565b600061248a6040518060800160405280600160ff1681526020018481526020018681526020018b60a00151815250611988565b905060006124a06004546001600160a01b031690565b90506124b58a60000151828c60400151612e6f565b806001600160a01b031663c5a5ebda898c600001518d604001518e60800151898f896040518863ffffffff1660e01b81526004016124f8969594939291906139b1565b60206040518083038185885af1158015612516573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610d6d9190613a17565b60006125488260206138fd565b835110156125905760405162461bcd60e51b8152602060048201526015602482015274746f427974657333325f6f75744f66426f756e647360581b60448201526064016107f3565b50016020015190565b60006125a68260026138fd565b835110156125ed5760405162461bcd60e51b8152602060048201526014602482015273746f55696e7431365f6f75744f66426f756e647360601b60448201526064016107f3565b50016002015190565b60006001600160a01b03198216156126465760405162461bcd60e51b8152602060048201526013602482015272696e76616c69642045564d206164647265737360681b60448201526064016107f3565b5090565b606060008060006126636003546001600160a01b031690565b6001600160a01b031663a9e11893866040518263ffffffff1660e01b815260040161268e9190613665565b600060405180830381865afa1580156126ab573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526126d39190810190613b56565b905060006126e48260e00151610d7b565b90506126ef81611327565b6127325760405162461bcd60e51b81526020600482015260146024820152731d1bdad95b881b9bdd081c9959da5cdd195c995960621b60448201526064016107f3565b600061273d82612d8b565b905060006127536004546001600160a01b031690565b90506000816001600160a01b031663c3f511c18a6040518263ffffffff1660e01b81526004016127839190613665565b6000604051808303816000875af11580156127a2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526127ca9190810190613c7a565b90506000836127d886612d8b565b6127e291906138cd565b90506000836001600160a01b031663ea63738d846040518263ffffffff1660e01b81526004016128129190613665565b600060405180830381865afa15801561282f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526128579190810190613cae565b9050612877876060015161ffff1660009081526007602052604090205490565b8160c00151146128c95760405162461bcd60e51b815260206004820152601760248201527f636f6e7472616374206e6f74207265676973746572656400000000000000000060448201526064016107f3565b8660a001516001600160401b03168760800151886060015161ffff167fcaf280c8cfeba144da67230d9b009c8f868a75bac9a528fa0474be1ba317c16960405160405180910390a460e001519a9099509397509295505050505050565b34156129805760405162461bcd60e51b815260206004820152602360248201527f726563697069656e742063616e6e6f742073776170206e61746976652061737360448201526265747360e81b60648201526084016107f3565b6000546001600160a01b0363010000009091048116908516811480156129a35750815b15612a3e57604051632e1a7d4d60e01b8152600481018490526001600160a01b03821690632e1a7d4d90602401600060405180830381600087803b1580156129ea57600080fd5b505af11580156129fe573d6000803e3d6000fd5b50506040516001600160a01b038716925085156108fc02915085906000818181858888f19350505050158015612a38573d6000803e3d6000fd5b50612a49565b612a49858585612ba8565b5050505050565b3415612a935760405162461bcd60e51b815260206004820152601260248201527176616c7565206d757374206265207a65726f60701b60448201526064016107f3565b8015612b7957600054604051632e1a7d4d60e01b81526004810186905263010000009091046001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015612ae557600080fd5b505af1158015612af9573d6000803e3d6000fd5b50505050826001600160a01b03166108fc8386612b1691906138cd565b6040518115909202916000818181858888f19350505050158015612b3e573d6000803e3d6000fd5b508115612b7457604051339083156108fc029084906000818181858888f19350505050158015612b72573d6000803e3d6000fd5b505b611f23565b600054630100000090046001600160a01b0316612b9b818561126386896138cd565b8215612a4957612a498133855b6040516001600160a01b03831660248201526044810182905261087f90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612f84565b6000612c188260016138fd565b83511015612c5e5760405162461bcd60e51b8152602060048201526013602482015272746f55696e74385f6f75744f66426f756e647360681b60448201526064016107f3565b50016001015190565b6000612c748260206138fd565b835110156125905760405162461bcd60e51b8152602060048201526015602482015274746f55696e743235365f6f75744f66426f756e647360581b60448201526064016107f3565b6001600160a01b0381166000908152600b602052604090205460ff1615612d255760405162461bcd60e51b815260206004820152601860248201527f746f6b656e20616c72656164792072656769737465726564000000000000000060448201526064016107f3565b6001600160a01b03166000818152600b60205260408120805460ff19166001908117909155600c805491820181559091527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c70180546001600160a01b0319169091179055565b604080513060248083019190915282518083039091018152604490910182526020810180516001600160e01b03166370a0823160e01b179052905160009182916001600160a01b03851691612ddf91613968565b600060405180830381855afa9150503d8060008114612e1a576040519150601f19603f3d011682016040523d82523d6000602084013e612e1f565b606091505b50915050808060200190518101906121e49190613759565b6040516001600160a01b0380851660248301528316604482015260648101829052611f239085906323b872dd60e01b90608401612bd4565b801580612ee95750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015612ec3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ee79190613759565b155b612f545760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b60648201526084016107f3565b6040516001600160a01b03831660248201526044810182905261087f90849063095ea7b360e01b90606401612bd4565b6000612fd9826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166130569092919063ffffffff16565b80519091501561087f5780806020019051810190612ff79190613d76565b61087f5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016107f3565b6060612219848460008585600080866001600160a01b0316858760405161307d9190613968565b60006040518083038185875af1925050503d80600081146130ba576040519150601f19603f3d011682016040523d82523d6000602084013e6130bf565b606091505b50915091506130d0878383876130db565b979650505050505050565b6060831561314a578251600003613143576001600160a01b0385163b6131435760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016107f3565b5081612219565b612219838381511561315f5781518083602001fd5b8060405162461bcd60e51b81526004016107f39190613665565b61ffff8116811461318957600080fd5b50565b6001600160a01b038116811461318957600080fd5b600080604083850312156131b457600080fd5b82356131bf81613179565b915060208301356131cf8161318c565b809150509250929050565b63ffffffff8116811461318957600080fd5b60008060008060008060c0878903121561320557600080fd5b86356132108161318c565b95506020870135945060408701359350606087013561322e81613179565b92506080870135915060a0870135613245816131da565b809150509295509295509295565b60ff8116811461318957600080fd5b6000806040838503121561327557600080fd5b8235915060208301356131cf81613253565b6000806000806080858703121561329d57600080fd5b8435935060208501356132af81613179565b92506040850135915060608501356132c6816131da565b939692955090935050565b6000602082840312156132e357600080fd5b81356121e48161318c565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b0381118282101715613326576133266132ee565b60405290565b60405161016081016001600160401b0381118282101715613326576133266132ee565b60405161010081016001600160401b0381118282101715613326576133266132ee565b604051601f8201601f191681016001600160401b038111828210171561339a5761339a6132ee565b604052919050565b60006001600160401b038211156133bb576133bb6132ee565b50601f01601f191660200190565b6000602082840312156133db57600080fd5b81356001600160401b038111156133f157600080fd5b8201601f8101841361340257600080fd5b8035613415613410826133a2565b613372565b81815285602083850101111561342a57600080fd5b81602084016020830137600091810160200191909152949350505050565b6000806040838503121561345b57600080fd5b823561346681613179565b946020939093013593505050565b6000806020838503121561348757600080fd5b82356001600160401b038082111561349e57600080fd5b818501915085601f8301126134b257600080fd5b8135818111156134c157600080fd5b8660208285010111156134d357600080fd5b60209290920196919550909350505050565b6000806000606084860312156134fa57600080fd5b833561350581613179565b925060208401356135158161318c565b9150604084013561352581613253565b809150509250925092565b60006020828403121561354257600080fd5b81356121e481613179565b801515811461318957600080fd5b6000806040838503121561356e57600080fd5b823561357981613179565b915060208301356131cf8161354d565b6000806040838503121561359c57600080fd5b82356134668161318c565b6000608082840312156135b957600080fd5b604051608081018181106001600160401b03821117156135db576135db6132ee565b60405282356135e981613253565b808252506020830135602082015260408301356040820152606083013560608201528091505092915050565b60005b83811015613630578181015183820152602001613618565b50506000910152565b60008151808452613651816020860160208601613615565b601f01601f19169290920160200192915050565b6020815260006121e46020830184613639565b6020808252825182820181905260009190848201906040850190845b818110156136b95783516001600160a01b031683529284019291840191600101613694565b50909695505050505050565b6000806000606084860312156136da57600080fd5b83356136e581613179565b925060208401356136f58161318c565b929592945050506040919091013590565b60208082526014908201527331b0b63632b9103737ba103a34329037bbb732b960611b604082015260600190565b6020808252600b908201526a3bb937b7339031b430b4b760a91b604082015260600190565b60006020828403121561376b57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b60ff8281168282160390811115610af757610af7613772565b600181815b808511156137dc5781600019048211156137c2576137c2613772565b808516156137cf57918102915b93841c93908002906137a6565b509250929050565b6000826137f357506001610af7565b8161380057506000610af7565b816001811461381657600281146138205761383c565b6001915050610af7565b60ff84111561383157613831613772565b50506001821b610af7565b5060208310610133831016604e8410600b841016171561385f575081810a610af7565b61386983836137a1565b806000190482111561387d5761387d613772565b029392505050565b60006121e460ff8416836137e4565b6000826138b157634e487b7160e01b600052601260045260246000fd5b500490565b8082028115828204841417610af757610af7613772565b81810381811115610af757610af7613772565b6000602082840312156138f257600080fd5b81516121e48161318c565b80820180821115610af757610af7613772565b6020808252601290820152711d1bdad95b881b9bdd081858d8d95c1d195960721b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052603160045260246000fd5b6000825161397a818460208701613615565b9190910192915050565b805161398f81613253565b919050565b6000602082840312156139a657600080fd5b81516121e481613253565b60018060a01b038716815285602082015261ffff8516604082015283606082015263ffffffff8316608082015260c060a082015260006139f460c0830184613639565b98975050505050505050565b80516001600160401b038116811461398f57600080fd5b600060208284031215613a2957600080fd5b6121e482613a00565b805161398f816131da565b805161398f81613179565b600082601f830112613a5957600080fd5b8151613a67613410826133a2565b818152846020838601011115613a7c57600080fd5b612219826020830160208701613615565b600082601f830112613a9e57600080fd5b815160206001600160401b03821115613ab957613ab96132ee565b613ac7818360051b01613372565b82815260079290921b84018101918181019086841115613ae657600080fd5b8286015b84811015613b4b5760808189031215613b035760008081fd5b613b0b613304565b815181528482015185820152604080830151613b2681613253565b90820152606082810151613b3981613253565b90820152835291830191608001613aea565b509695505050505050565b600060208284031215613b6857600080fd5b81516001600160401b0380821115613b7f57600080fd5b908301906101608286031215613b9457600080fd5b613b9c61332c565b613ba583613984565b8152613bb360208401613a32565b6020820152613bc460408401613a32565b6040820152613bd560608401613a3d565b606082015260808301516080820152613bf060a08401613a00565b60a0820152613c0160c08401613984565b60c082015260e083015182811115613c1857600080fd5b613c2487828601613a48565b60e083015250610100613c38818501613a32565b908201526101208381015183811115613c5057600080fd5b613c5c88828701613a8d565b91830191909152506101409283015192810192909252509392505050565b600060208284031215613c8c57600080fd5b81516001600160401b03811115613ca257600080fd5b61221984828501613a48565b600060208284031215613cc057600080fd5b81516001600160401b0380821115613cd757600080fd5b908301906101008286031215613cec57600080fd5b613cf461334f565b613cfd83613984565b81526020830151602082015260408301516040820152613d1f60608401613a3d565b606082015260808301516080820152613d3a60a08401613a3d565b60a082015260c083015160c082015260e083015182811115613d5b57600080fd5b613d6787828601613a48565b60e08301525095945050505050565b600060208284031215613d8857600080fd5b81516121e48161354d56fea2646970667358221220043ecf0d1429bfc7866a5607e98ae2b1cca0eeef46c7279047c0211efe0f076764736f6c63430008110033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000000000000000000000000000000200000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b0000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000001
-----Decoded View---------------
Arg [0] : chainId (uint16): 2
Arg [1] : wormhole (address): 0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B
Arg [2] : tokenBridge_ (address): 0x3ee18B2214AFF97000D974cf647E7C347E8fa585
Arg [3] : wethAddress (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [4] : unwrapWeth_ (bool): True
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [1] : 00000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b
Arg [2] : 0000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585
Arg [3] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000001
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.