Overview
ETH Balance
ETH Value
$0.00More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 18 internal transactions
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
18295588 | 6 hrs ago | 0 ETH | ||||
18295588 | 6 hrs ago | 0 ETH | ||||
18295588 | 6 hrs ago | 0 ETH | ||||
18295588 | 6 hrs ago | 0 ETH | ||||
18295588 | 6 hrs ago | 0 ETH | ||||
18295588 | 6 hrs ago | 0 ETH | ||||
18295588 | 6 hrs ago | 0 ETH | ||||
18262183 | 31 hrs ago | 0 ETH | ||||
18262183 | 31 hrs ago | 0 ETH | ||||
18262183 | 31 hrs ago | 0 ETH | ||||
18261592 | 31 hrs ago | 0 ETH | ||||
18261592 | 31 hrs ago | 0 ETH | ||||
18261592 | 31 hrs ago | 0 ETH | ||||
18261592 | 31 hrs ago | 0 ETH | ||||
18261592 | 31 hrs ago | 0 ETH | ||||
18261592 | 31 hrs ago | 0 ETH | ||||
18254497 | 38 hrs ago | Contract Creation | 0 ETH | |||
18254497 | 38 hrs ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
DaimoPay
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 999999 runs
Other Settings:
london EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.12; import "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; import "openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol"; import "./DaimoPayBridger.sol"; import "./DaimoPayExecutor.sol"; import "./PayIntentFactory.sol"; import "./TokenUtils.sol"; // A Daimo Pay transfer has 4 steps: // 1. Alice sends (tokenIn, amountIn) to the intent address on chain A. This is // a simple erc20 transfer. // 2. Relayer swaps tokenIn to bridgeTokenIn and initiates the bridge using // startIntent. The intent commits to a destination bridgeTokenOut, and the // bridger guarantees this amount will show up on chain B (or reverts if the // amount of bridgeTokenIn is insufficient). // 3. Relayer immediately calls fastFinishIntent on chain B, paying Bob. // 4. Finally, the slow bridge transfer arrives on chain B later, and the // relayer can call claimIntent. // For simplicity, a same-chain Daimo Pay transfer follows the same steps. // Instead of swap+bridge, startIntent only swaps and verifies correct output. // FastFinish remains optional but is unnecessary. Claim completes the intent. /// @author Daimo, Inc /// @custom:security-contact [email protected] /// @notice Enables fast cross-chain transfers with optimistic intents. /// WARNING: Never approve tokens directly to this contract. Never transfer /// tokens to this contract as a standalone transaction. Such tokens can be /// stolen by anyone. Instead: /// - Users should only interact by sending funds to an intent address. /// - Relayers should transfer funds and call this contract atomically via their /// own contracts. /// /// @dev Allows optimistic fast intents. Alice initiates a transfer by calling /// `startIntent` on chain A. After the bridging delay (e.g. 10+ min for CCTP), /// funds arrive at the intent address deployed on chain B. Bob (or anyone) can /// call `claimIntent` on chain B to finish her intent. /// /// Alternatively, immediately after the first call, a relayer can call /// `fastFinishIntent` to finish Alice's intent immediately. Later, when the /// funds arrive from the bridge, the relayer will call `claimIntent` to get /// repaid for their fast-finish. contract DaimoPay is ReentrancyGuard { using SafeERC20 for IERC20; address constant ADDR_MAX = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; /// Efficiently generates + deploys CREATE2 intent addresses. PayIntentFactory public immutable intentFactory; /// Contract that executes arbitrary contract calls on behalf of the /// DaimoPay escrow contract. DaimoPayExecutor public immutable executor; /// On the source chain, record intents that have been sent. mapping(address intentAddr => bool) public intentSent; /// On the destination chain, record the status of intents: /// - address(0) = not finished. /// - Relayer address = fast-finished, awaiting claim to repay relayer. /// - ADDR_MAX = claimed. any additional funds received are refunded. mapping(address intentAddr => address) public intentToRecipient; /// Intent initiated on chain A event Start(address indexed intentAddr, PayIntent intent); /// Intent completed ~immediately on chain B event FastFinish(address indexed intentAddr, address indexed newRecipient); /// Intent settled later, once the underlying bridge transfer completes. /// Record the final recipient of the claim: /// - If fast finished, the relayer. /// - Otherwise, the original recipient (Bob). event Claim(address indexed intentAddr, address indexed finalRecipient); /// When the intent is completed, emit this event. `success=false` indicates /// that the final call reverted, and funds were refunded to refundAddr. event IntentFinished( address indexed intentAddr, address indexed destinationAddr, bool indexed success, PayIntent intent ); /// When a double-paid intent is refunded, emit this event event IntentRefunded( address indexed intentAddr, address indexed refundAddr, IERC20[] tokens, uint256[] amounts, PayIntent intent ); constructor(PayIntentFactory _intentFactory) { intentFactory = _intentFactory; executor = new DaimoPayExecutor(address(this)); } /// Starts an intent, bridging to the destination chain if necessary. function startIntent( PayIntent calldata intent, IERC20[] calldata paymentTokens, Call[] calldata calls, bytes calldata bridgeExtraData ) public nonReentrant { require( block.timestamp < intent.expirationTimestamp, "DP: intent expired" ); PayIntentContract intentContract = intentFactory.createIntent(intent); // Ensure we don't reuse a nonce in the case where Alice is sending to // same destination with the same nonce multiple times. require(!intentSent[address(intentContract)], "DP: already sent"); // Can't call startIntent if the intent has already been claimed. require( intentToRecipient[address(intentContract)] != ADDR_MAX, "DP: already claimed" ); // Mark the intent as sent intentSent[address(intentContract)] = true; // Transfer from intent contract to the executor contract to run // relayer-provided calls. intentContract.sendTokens({ intent: intent, tokens: paymentTokens, recipient: payable(address(executor)) }); if (intent.toChainId == block.chainid) { // Same chain. Swap the tokens to one of the bridgeTokenOutOptions // and send them back to the intent contract for later claimIntent. // Run arbitrary calls provided by the relayer. These will generally // approve the swap contract and swap if necessary. // The executor contract checks that at least one of the // bridgeTokenOutOptions is present. Any surplus tokens are given // to the caller. executor.execute({ calls: calls, expectedOutput: intent.bridgeTokenOutOptions, recipient: payable(address(intentContract)), surplusRecipient: payable(msg.sender) }); } else { // Different chains. Get the input token and amount required to // initiate bridging IDaimoPayBridger bridger = intent.bridger; (address bridgeTokenIn, uint256 inAmount) = bridger .getBridgeTokenIn({ toChainId: intent.toChainId, bridgeTokenOutOptions: intent.bridgeTokenOutOptions }); // Run arbitrary calls provided by the relayer. These will generally // approve the swap contract and swap if necessary. // The executor contract checks that the output is sufficient. Any // surplus tokens are given to the caller. TokenAmount[] memory expectedOutput = new TokenAmount[](1); expectedOutput[0] = TokenAmount({ token: IERC20(bridgeTokenIn), amount: inAmount }); executor.execute({ calls: calls, expectedOutput: expectedOutput, recipient: payable(address(this)), surplusRecipient: payable(msg.sender) }); // Approve bridger and initiate bridging IERC20(bridgeTokenIn).forceApprove({ spender: address(bridger), value: inAmount }); bridger.sendToChain({ toChainId: intent.toChainId, toAddress: address(intentContract), bridgeTokenOutOptions: intent.bridgeTokenOutOptions, extraData: bridgeExtraData }); } emit Start({intentAddr: address(intentContract), intent: intent}); } /// The relayer calls this function to complete an intent immediately on /// the destination chain. /// /// The relayer must call this function and transfer the necessary tokens to /// this contract in the same transaction. This function executes arbitrary /// calls specified by the relayer, e.g. to convert the transferred tokens /// into the required amount of finalCallToken. /// /// Later, when the slower bridge transfer arrives, the relayer will be able /// to claim the bridged tokens. function fastFinishIntent( PayIntent calldata intent, Call[] calldata calls, IERC20[] calldata tokens ) public nonReentrant { require(intent.toChainId == block.chainid, "DP: wrong chain"); require( block.timestamp < intent.expirationTimestamp, "DP: intent expired" ); // Calculate intent address address intentAddr = intentFactory.getIntentAddress(intent); // Optimistic fast finish is only for transfers which haven't already // been fastFinished or claimed. require( intentToRecipient[intentAddr] == address(0), "DP: already finished" ); // Record relayer as new recipient when the bridged tokens arrive intentToRecipient[intentAddr] = msg.sender; // Transfer tokens to the executor contract to run relayer-provided // calls in _finishIntent. uint256 n = tokens.length; for (uint256 i = 0; i < n; ++i) { TokenUtils.transferBalance({ token: tokens[i], recipient: payable(address(executor)) }); } // Finish the intent and return any leftover tokens to the caller _finishIntent({intentAddr: intentAddr, intent: intent, calls: calls}); emit FastFinish({intentAddr: intentAddr, newRecipient: msg.sender}); } /// Completes an intent, claiming funds. The bridge transfer must already /// have been completed--tokens are already in the intent contract. /// /// If FastFinish happened for this intent, then the recipient is the /// relayer who fastFinished the intent. Otherwise, the recipient remains /// the original address. function claimIntent( PayIntent calldata intent, Call[] calldata calls ) public nonReentrant { require(intent.toChainId == block.chainid, "DP: wrong chain"); require( block.timestamp < intent.expirationTimestamp, "DP: intent expired" ); PayIntentContract intentContract = intentFactory.createIntent(intent); // Check the recipient for this intent. address recipient = intentToRecipient[address(intentContract)]; // If intent is double-paid after it has already been claimed, then // the recipient should call refundIntent to get their funds back. require(recipient != ADDR_MAX, "DP: already claimed"); // Mark intent as claimed intentToRecipient[address(intentContract)] = ADDR_MAX; if (recipient == address(0)) { // No relayer showed up, so just complete the intent. recipient = intent.finalCall.to; // Send tokens from the intent contract to the executor contract // to run relayer-provided calls in _finishIntent. // The intent contract will check that sufficient bridge tokens // were received. intentContract.checkBalanceAndSendTokens({ intent: intent, tokenAmounts: intent.bridgeTokenOutOptions, recipient: payable(address(executor)) }); // Complete the intent and return any leftover tokens to the caller _finishIntent({ intentAddr: address(intentContract), intent: intent, calls: calls }); } else { // Otherwise, the relayer fastFinished the intent. Repay them. // The intent contract will check that sufficient bridge tokens // were received. intentContract.checkBalanceAndSendTokens({ intent: intent, tokenAmounts: intent.bridgeTokenOutOptions, recipient: payable(recipient) }); } emit Claim({ intentAddr: address(intentContract), finalRecipient: recipient }); } /// Refund a double-paid intent. On the source chain, refund only if the /// intent has already been started. On the destination chain, refund only /// if the intent has already been claimed. function refundIntent( PayIntent calldata intent, IERC20[] calldata tokens ) public nonReentrant { PayIntentContract intentContract = intentFactory.createIntent(intent); address intentAddr = address(intentContract); bool expired = block.timestamp >= intent.expirationTimestamp; if (intent.toChainId == block.chainid) { // Refund only if already claimed or the intent has expired. bool claimed = intentToRecipient[address(intentContract)] == ADDR_MAX; require(claimed || expired, "DP: not claimed"); } else { // Refund only if already started or the intent has expired. require(intentSent[intentAddr] || expired, "DP: not started"); } // Send tokens directly from intent contract to the refund address. uint256[] memory amounts = intentContract.sendTokens({ intent: intent, tokens: tokens, recipient: payable(intent.refundAddress) }); emit IntentRefunded({ intentAddr: intentAddr, refundAddr: intent.refundAddress, tokens: tokens, amounts: amounts, intent: intent }); } /// Execute the calls provided by the relayer and check that there is /// sufficient finalCallToken. Then, if the intent has a finalCall, make /// the intent call. Otherwise, transfer the token to the final address. /// Any surplus tokens are given to the caller. /// This function assumes that tokens are already transferred to the /// executor contract before being called. function _finishIntent( address intentAddr, PayIntent calldata intent, Call[] calldata calls ) internal { // Run arbitrary calls provided by the relayer. These will generally // approve the swap contract and swap if necessary. Any surplus tokens // are given to the caller. TokenAmount[] memory finalCallAmount = new TokenAmount[](1); finalCallAmount[0] = intent.finalCallToken; executor.execute({ calls: calls, expectedOutput: finalCallAmount, recipient: payable(address(this)), surplusRecipient: payable(msg.sender) }); bool success; if (intent.finalCall.data.length > 0) { // If the intent is a call, approve the final token and make the call success = executor.executeFinalCall({ finalCall: intent.finalCall, finalCallToken: intent.finalCallToken }); } else { // If the final call is a transfer, transfer the token. success = TokenUtils.tryTransfer( intent.finalCallToken.token, payable(intent.finalCall.to), intent.finalCallToken.amount ); } // If the call fails, transfer the token to the refund address if (!success) { TokenUtils.transfer({ token: intent.finalCallToken.token, recipient: payable(intent.refundAddress), amount: intent.finalCallToken.amount }); } emit IntentFinished({ intentAddr: intentAddr, destinationAddr: intent.finalCall.to, success: success, intent: intent }); } receive() external payable {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC1363} from "../../../interfaces/IERC1363.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC-20 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 { /** * @dev An operation with an ERC-20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful. */ function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) { return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful. */ function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) { return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. * * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client" * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. * * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client" * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. * * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being * set here. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * Reverts if the returned value is other than `true`. */ function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal { if (to.code.length == 0) { safeTransfer(token, to, value); } else if (!token.transferAndCall(to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * Reverts if the returned value is other than `true`. */ function transferFromAndCallRelaxed( IERC1363 token, address from, address to, uint256 value, bytes memory data ) internal { if (to.code.length == 0) { safeTransferFrom(token, from, to, value); } else if (!token.transferFromAndCall(from, to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}. * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall} * once without retrying, and relies on the returned value to be true. * * Reverts if the returned value is other than `true`. */ function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal { if (to.code.length == 0) { forceApprove(token, to, value); } else if (!token.approveAndCall(to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @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). * * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements. */ function _callOptionalReturn(IERC20 token, bytes memory data) private { uint256 returnSize; uint256 returnValue; assembly ("memory-safe") { let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20) // bubble errors if iszero(success) { let ptr := mload(0x40) returndatacopy(ptr, 0, returndatasize()) revert(ptr, returndatasize()) } returnSize := returndatasize() returnValue := mload(0) } if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) { revert SafeERC20FailedOperation(address(token)); } } /** * @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). * * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { bool success; uint256 returnSize; uint256 returnValue; assembly ("memory-safe") { success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20) returnSize := returndatasize() returnValue := mload(0) } return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol) pragma solidity ^0.8.20; /** * @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 EIP-1153 (transient storage) is available on the chain you're deploying at, * consider using {ReentrancyGuardTransient} instead. * * 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; /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); 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 if (_status == ENTERED) { revert ReentrancyGuardReentrantCall(); } // 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; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == ENTERED; } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.12; import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; import "./TokenUtils.sol"; import "./interfaces/IDaimoPayBridger.sol"; /// @author Daimo, Inc /// @custom:security-contact [email protected] /// /// @notice Bridges assets from the current chain to a supported destination /// chain. Multiplexes between bridging protocols by destination chain. contract DaimoPayBridger is IDaimoPayBridger { using SafeERC20 for IERC20; /// Map chainId to IDaimoPayBridger implementation. mapping(uint256 chainId => IDaimoPayBridger bridger) public chainIdToBridger; /// Specify the bridger implementation to use for each chain. constructor( uint256[] memory _toChainIds, IDaimoPayBridger[] memory _bridgers ) { uint256 n = _toChainIds.length; require(n == _bridgers.length, "DPB: wrong bridgers length"); for (uint256 i = 0; i < n; ++i) { chainIdToBridger[_toChainIds[i]] = _bridgers[i]; } } // ----- BRIDGER FUNCTIONS ----- /// Get the input token and amount required to achieve one of the given /// output options on a given chain. function getBridgeTokenIn( uint256 toChainId, TokenAmount[] calldata bridgeTokenOutOptions ) external view returns (address bridgeTokenIn, uint256 inAmount) { IDaimoPayBridger bridger = chainIdToBridger[toChainId]; require(address(bridger) != address(0), "DPB: missing bridger"); return bridger.getBridgeTokenIn(toChainId, bridgeTokenOutOptions); } /// Initiate a bridge to a supported destination chain. function sendToChain( uint256 toChainId, address toAddress, TokenAmount[] calldata bridgeTokenOutOptions, bytes calldata extraData ) public { require(toChainId != block.chainid, "DPB: same chain"); // Get the specific bridger implementation for toChain (CCTP, Across, // Axelar, etc) IDaimoPayBridger bridger = chainIdToBridger[toChainId]; require(address(bridger) != address(0), "DPB: missing bridger"); // Move input token from caller to this contract and initiate bridging. (address bridgeTokenIn, uint256 inAmount) = this.getBridgeTokenIn({ toChainId: toChainId, bridgeTokenOutOptions: bridgeTokenOutOptions }); require(bridgeTokenIn != address(0), "DPB: missing bridge token in"); IERC20(bridgeTokenIn).safeTransferFrom({ from: msg.sender, to: address(this), value: inAmount }); // Approve tokens to the bridge contract and intiate bridging. IERC20(bridgeTokenIn).forceApprove({ spender: address(bridger), value: inAmount }); bridger.sendToChain({ toChainId: toChainId, toAddress: toAddress, bridgeTokenOutOptions: bridgeTokenOutOptions, extraData: extraData }); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.12; import "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; import "openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol"; import "./TokenUtils.sol"; /// Represents a contract call. struct Call { /// Address of the contract to call. address to; /// Native token amount for call, or 0 uint256 value; /// Calldata for call bytes data; } /// @author Daimo, Inc /// @custom:security-contact [email protected] /// @notice This contract is used to execute arbitrary contract calls on behalf /// of the DaimoPay escrow contract. /// WARNING: Never approve tokens directly to this contract. Never transfer /// tokens to this contract. Such tokens can be stolen by anyone. All /// interactions with this contract should be done via the DaimoPay contract. contract DaimoPayExecutor is ReentrancyGuard { using SafeERC20 for IERC20; /// The only address that is allowed to call the `execute` function. address public immutable escrow; constructor(address _escrow) { escrow = _escrow; } /// Execute arbitrary calls. Revert if any fail. /// Check that at least one of the expectedOutput tokens is present. Assumes /// that exactly one token is present and transfers it to the recipient. /// Returns any surplus tokens to the surplus recipient. function execute( Call[] calldata calls, TokenAmount[] calldata expectedOutput, address payable recipient, address payable surplusRecipient ) external nonReentrant { require(msg.sender == escrow, "DPCE: only escrow"); // Execute provided calls. uint256 callsLength = calls.length; for (uint256 i = 0; i < callsLength; ++i) { Call calldata call = calls[i]; (bool success, ) = call.to.call{value: call.value}(call.data); require(success, "DPCE: call failed"); } /// Check that at least one of the expectedOutput tokens is present /// with enough balance. uint256 outputIndex = TokenUtils.checkBalance({ tokenAmounts: expectedOutput }); require( outputIndex < expectedOutput.length, "DPCE: insufficient output" ); // Transfer the expected amount of the token to the recipient. TokenUtils.transfer({ token: expectedOutput[outputIndex].token, recipient: recipient, amount: expectedOutput[outputIndex].amount }); // Transfer any surplus tokens to the surplus recipient. TokenUtils.transferBalance({ token: expectedOutput[outputIndex].token, recipient: surplusRecipient }); } /// Execute a final call. Approve the final token and make the call. /// Return whether the call succeeded. function executeFinalCall( Call calldata finalCall, TokenAmount calldata finalCallToken ) external nonReentrant returns (bool success) { require(msg.sender == escrow, "DPCE: only escrow"); // Approve the final call token to the final call contract. TokenUtils.approve({ token: finalCallToken.token, spender: address(finalCall.to), amount: finalCallToken.amount }); // Then, execute the final call. (success, ) = finalCall.to.call{value: finalCall.value}(finalCall.data); } /// Accept native-token (eg ETH) inputs receive() external payable {} }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.12; import "openzeppelin-contracts/contracts/utils/Create2.sol"; import "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "./PayIntent.sol"; /// @author Daimo, Inc /// @custom:security-contact [email protected] /// @notice Factory for intent addresses. contract PayIntentFactory { PayIntentContract public immutable intentImpl; constructor() { intentImpl = new PayIntentContract(); } /// Deploy a proxy for the intent contract implementation to the CREATE2 /// address for the given intent. function createIntent( PayIntent calldata intent ) public returns (PayIntentContract ret) { address intentAddr = getIntentAddress(intent); if (intentAddr.code.length > 0) { // Handling this case allows eg. start+claim in a single tx. // This allows more efficient relaying & easier unit testing. // See https://github.com/foundry-rs/foundry/issues/8485 return PayIntentContract(payable(intentAddr)); } ret = PayIntentContract( payable( address( new ERC1967Proxy{salt: bytes32(0)}( address(intentImpl), abi.encodeCall( PayIntentContract.initialize, (calcIntentHash(intent)) ) ) ) ) ); } /// Compute the deterministic CREATE2 address of the intent contract for /// the given intent. function getIntentAddress( PayIntent calldata intent ) public view returns (address) { return Create2.computeAddress( 0, keccak256( abi.encodePacked( type(ERC1967Proxy).creationCode, abi.encode( address(intentImpl), abi.encodeCall( PayIntentContract.initialize, (calcIntentHash(intent)) ) ) ) ) ); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.12; import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; /// Asset amount, e.g. $100 USDC or 0.1 ETH struct TokenAmount { /// Zero address = native asset, e.g. ETH IERC20 token; uint256 amount; } /// Utility functions that work for both ERC20 and native tokens. library TokenUtils { using SafeERC20 for IERC20; /// Returns ERC20 or ETH balance. function getBalanceOf( IERC20 token, address addr ) internal view returns (uint256) { if (address(token) == address(0)) { return addr.balance; } else { return token.balanceOf(addr); } } /// Approves a token transfer. function approve(IERC20 token, address spender, uint256 amount) internal { if (address(token) != address(0)) { token.forceApprove({spender: spender, value: amount}); } // Do nothing for native token. } /// Sends an ERC20 or ETH transfer. For ETH, verify call success. function transfer( IERC20 token, address payable recipient, uint256 amount ) internal { if (address(token) != address(0)) { token.safeTransfer({to: recipient, value: amount}); } else { // Native token transfer (bool success, ) = recipient.call{value: amount}(""); require(success, "TokenUtils: ETH transfer failed"); } } /// Sends an ERC20 or ETH transfer. Returns true if successful. function tryTransfer( IERC20 token, address recipient, uint256 amount ) internal returns (bool) { if (address(token) != address(0)) { return token.trySafeTransfer({to: recipient, value: amount}); } else { (bool success, ) = recipient.call{value: amount}(""); return success; } } /// Sends an ERC20 transfer. function transferFrom( IERC20 token, address from, address to, uint256 amount ) internal { require( address(token) != address(0), "TokenUtils: ETH transferFrom must be caller" ); token.safeTransferFrom({from: from, to: to, value: amount}); } /// Sends any token balance in the contract to the recipient. function transferBalance( IERC20 token, address payable recipient ) internal returns (uint256) { uint256 balance = getBalanceOf({token: token, addr: address(this)}); if (balance > 0) { transfer({token: token, recipient: recipient, amount: balance}); } return balance; } /// Check that the address has enough of at least one of the tokenAmounts. /// Returns the index of the first token that has sufficient balance, or /// the length of the tokenAmounts array if no token has sufficient balance. function checkBalance( TokenAmount[] calldata tokenAmounts ) internal view returns (uint256) { uint256 n = tokenAmounts.length; for (uint256 i = 0; i < n; ++i) { TokenAmount calldata tokenAmount = tokenAmounts[i]; uint256 balance = getBalanceOf({ token: tokenAmount.token, addr: address(this) }); if (balance >= tokenAmount.amount) { return i; } } return n; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-20 standard as defined in the ERC. */ 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 value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` 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 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol) pragma solidity ^0.8.20; import {IERC20} from "./IERC20.sol"; import {IERC165} from "./IERC165.sol"; /** * @title IERC1363 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363]. * * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction. */ interface IERC1363 is IERC20, IERC165 { /* * Note: the ERC-165 identifier for this interface is 0xb0202a11. * 0xb0202a11 === * bytes4(keccak256('transferAndCall(address,uint256)')) ^ * bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^ * bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^ * bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^ * bytes4(keccak256('approveAndCall(address,uint256)')) ^ * bytes4(keccak256('approveAndCall(address,uint256,bytes)')) */ /** * @dev Moves a `value` amount of tokens from the caller's account to `to` * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferAndCall(address to, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from the caller's account to `to` * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @param data Additional data with no specified format, sent in call to `to`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param from The address which you want to send tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferFromAndCall(address from, address to, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param from The address which you want to send tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @param data Additional data with no specified format, sent in call to `to`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function approveAndCall(address spender, uint256 value) external returns (bool); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @param data Additional data with no specified format, sent in call to `spender`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.12; import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import "../TokenUtils.sol"; /// @author Daimo, Inc /// @custom:security-contact [email protected] /// @notice Bridges assets. Specifically, it lets any relayer initiate a bridge /// transaction to another chain. interface IDaimoPayBridger { /// Emitted when a bridge transaction is initiated event BridgeInitiated( address fromAddress, address fromToken, uint256 fromAmount, uint256 toChainId, address toAddress, address toToken, uint256 toAmount ); /// Determine the input token and amount required to achieve one of the /// given output options on a given chain. function getBridgeTokenIn( uint256 toChainId, TokenAmount[] memory bridgeTokenOutOptions ) external view returns (address bridgeTokenIn, uint256 inAmount); /// Initiate a bridge. Guarantee that one of the bridge token options /// (bridgeTokenOut, outAmount) shows up at toAddress on toChainId. /// Otherwise, revert. function sendToChain( uint256 toChainId, address toAddress, TokenAmount[] calldata bridgeTokenOutOptions, bytes calldata extraData ) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/Create2.sol) pragma solidity ^0.8.20; import {Errors} from "./Errors.sol"; /** * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer. * `CREATE2` can be used to compute in advance the address where a smart * contract will be deployed, which allows for interesting new mechanisms known * as 'counterfactual interactions'. * * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more * information. */ library Create2 { /** * @dev There's no code to deploy. */ error Create2EmptyBytecode(); /** * @dev Deploys a contract using `CREATE2`. The address where the contract * will be deployed can be known in advance via {computeAddress}. * * The bytecode for a contract can be obtained from Solidity with * `type(contractName).creationCode`. * * Requirements: * * - `bytecode` must not be empty. * - `salt` must have not been used for `bytecode` already. * - the factory must have a balance of at least `amount`. * - if `amount` is non-zero, `bytecode` must have a `payable` constructor. */ function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) { if (address(this).balance < amount) { revert Errors.InsufficientBalance(address(this).balance, amount); } if (bytecode.length == 0) { revert Create2EmptyBytecode(); } assembly ("memory-safe") { addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt) // if no address was created, and returndata is not empty, bubble revert if and(iszero(addr), not(iszero(returndatasize()))) { let p := mload(0x40) returndatacopy(p, 0, returndatasize()) revert(p, returndatasize()) } } if (addr == address(0)) { revert Errors.FailedDeployment(); } } /** * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the * `bytecodeHash` or `salt` will result in a new destination address. */ function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) { return computeAddress(salt, bytecodeHash, address(this)); } /** * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}. */ function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) { assembly ("memory-safe") { let ptr := mload(0x40) // Get free memory pointer // | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... | // |-------------------|---------------------------------------------------------------------------| // | bytecodeHash | CCCCCCCCCCCCC...CC | // | salt | BBBBBBBBBBBBB...BB | // | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA | // | 0xFF | FF | // |-------------------|---------------------------------------------------------------------------| // | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC | // | keccak(start, 85) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ | mstore(add(ptr, 0x40), bytecodeHash) mstore(add(ptr, 0x20), salt) mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff mstore8(start, 0xff) addr := and(keccak256(start, 85), 0xffffffffffffffffffffffffffffffffffffffff) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.2.0) (proxy/ERC1967/ERC1967Proxy.sol) pragma solidity ^0.8.22; import {Proxy} from "../Proxy.sol"; import {ERC1967Utils} from "./ERC1967Utils.sol"; /** * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an * implementation address that can be changed. This address is stored in storage in the location specified by * https://eips.ethereum.org/EIPS/eip-1967[ERC-1967], so that it doesn't conflict with the storage layout of the * implementation behind the proxy. */ contract ERC1967Proxy is Proxy { /** * @dev Initializes the upgradeable proxy with an initial implementation specified by `implementation`. * * If `_data` is nonempty, it's used as data in a delegate call to `implementation`. This will typically be an * encoded function call, and allows initializing the storage of the proxy like a Solidity constructor. * * Requirements: * * - If `data` is empty, `msg.value` must be zero. */ constructor(address implementation, bytes memory _data) payable { ERC1967Utils.upgradeToAndCall(implementation, _data); } /** * @dev Returns the current implementation address. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` */ function _implementation() internal view virtual override returns (address) { return ERC1967Utils.getImplementation(); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.12; import "openzeppelin-contracts/contracts/proxy/utils/Initializable.sol"; import "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; import "openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol"; import {Call} from "./DaimoPayExecutor.sol"; import "./TokenUtils.sol"; import "./interfaces/IDaimoPayBridger.sol"; /// Represents an intended call: "make X of token Y show up on chain Z, /// then [optionally] use it to do an arbitrary contract call". struct PayIntent { /// Intent only executes on given target chain. uint256 toChainId; /// Possible output tokens after bridging to the destination chain. /// Currently, native token is not supported as a bridge token output. TokenAmount[] bridgeTokenOutOptions; /// Expected token amount after swapping on the destination chain. TokenAmount finalCallToken; /// Contract call to execute on the destination chain. If finalCall.data is /// empty, the tokens are transferred to finalCall.to. Otherwise, (token, /// amount) is approved to finalCall.to and finalCall.to is called with /// finalCall.data and finalCall.value. Call finalCall; /// Escrow contract. All calls are made through this contract. address payable escrow; /// Bridger contract. IDaimoPayBridger bridger; /// Address to refund tokens if call fails, or zero. address refundAddress; /// Nonce. PayIntent receiving addresses are one-time use. uint256 nonce; /// Timestamp after which intent expires and can be refunded uint256 expirationTimestamp; } /// Calculates the intent hash of a PayIntent struct. function calcIntentHash(PayIntent calldata intent) pure returns (bytes32) { return keccak256(abi.encode(intent)); } /// @author Daimo, Inc /// @custom:security-contact [email protected] /// @notice This is an ephemeral intent contract. Any supported tokens sent to /// this address on any supported chain are forwarded, via a combination of /// bridging and swapping, into a specified call on a destination chain. contract PayIntentContract is Initializable, ReentrancyGuard { using SafeERC20 for IERC20; /// Save gas by minimizing storage to a single word. This makes intents /// usable on L1. intentHash = keccak(abi.encode(PayIntent)) bytes32 intentHash; /// Runs at deploy time. Singleton implementation contract = no init, /// no state. All other methods are called via proxy. constructor() { _disableInitializers(); } function initialize(bytes32 _intentHash) public initializer { intentHash = _intentHash; } /// Send tokens to a recipient. function sendTokens( PayIntent calldata intent, IERC20[] calldata tokens, address payable recipient ) public nonReentrant returns (uint256[] memory amounts) { require(calcIntentHash(intent) == intentHash, "PI: intent"); require(msg.sender == intent.escrow, "PI: only escrow"); uint256 n = tokens.length; amounts = new uint256[](n); // Send tokens to recipient for (uint256 i = 0; i < n; ++i) { amounts[i] = TokenUtils.transferBalance({ token: tokens[i], recipient: recipient }); } } /// Check that at least one of the token amounts is present. Assumes exactly /// one token is present, then sends the token to a recipient. function checkBalanceAndSendTokens( PayIntent calldata intent, TokenAmount[] calldata tokenAmounts, address payable recipient ) public nonReentrant { require(calcIntentHash(intent) == intentHash, "PI: intent"); require(msg.sender == intent.escrow, "PI: only escrow"); // Check that at least one of the token amounts is present. uint256 tokenIndex = TokenUtils.checkBalance({ tokenAmounts: tokenAmounts }); require(tokenIndex < tokenAmounts.length, "PI: insufficient balance"); // Transfer the token amount to the recipient. TokenUtils.transfer({ token: tokenAmounts[tokenIndex].token, recipient: recipient, amount: tokenAmounts[tokenIndex].amount }); } /// Accept native-token (eg ETH) inputs receive() external payable {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../token/ERC20/IERC20.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "../utils/introspection/IERC165.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol) pragma solidity ^0.8.20; /** * @dev Collection of common custom errors used in multiple contracts * * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library. * It is recommended to avoid relying on the error API for critical functionality. * * _Available since v5.1._ */ library Errors { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error InsufficientBalance(uint256 balance, uint256 needed); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedCall(); /** * @dev The deployment failed. */ error FailedDeployment(); /** * @dev A necessary precompile is missing. */ error MissingPrecompile(address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/Proxy.sol) pragma solidity ^0.8.20; /** * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to * be specified by overriding the virtual {_implementation} function. * * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a * different contract through the {_delegate} function. * * The success and return data of the delegated call will be returned back to the caller of the proxy. */ abstract contract Proxy { /** * @dev Delegates the current call to `implementation`. * * This function does not return to its internal call site, it will return directly to the external caller. */ function _delegate(address implementation) internal virtual { assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize()) // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize()) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } /** * @dev This is a virtual function that should be overridden so it returns the address to which the fallback * function and {_fallback} should delegate. */ function _implementation() internal view virtual returns (address); /** * @dev Delegates the current call to the address returned by `_implementation()`. * * This function does not return to its internal call site, it will return directly to the external caller. */ function _fallback() internal virtual { _delegate(_implementation()); } /** * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other * function in the contract matches the call data. */ fallback() external payable virtual { _fallback(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.2.0) (proxy/ERC1967/ERC1967Utils.sol) pragma solidity ^0.8.22; import {IBeacon} from "../beacon/IBeacon.sol"; import {IERC1967} from "../../interfaces/IERC1967.sol"; import {Address} from "../../utils/Address.sol"; import {StorageSlot} from "../../utils/StorageSlot.sol"; /** * @dev This library provides getters and event emitting update functions for * https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] slots. */ library ERC1967Utils { /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1. */ // solhint-disable-next-line private-vars-leading-underscore bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev The `implementation` of the proxy is invalid. */ error ERC1967InvalidImplementation(address implementation); /** * @dev The `admin` of the proxy is invalid. */ error ERC1967InvalidAdmin(address admin); /** * @dev The `beacon` of the proxy is invalid. */ error ERC1967InvalidBeacon(address beacon); /** * @dev An upgrade function sees `msg.value > 0` that may be lost. */ error ERC1967NonPayable(); /** * @dev Returns the current implementation address. */ function getImplementation() internal view returns (address) { return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value; } /** * @dev Stores a new address in the ERC-1967 implementation slot. */ function _setImplementation(address newImplementation) private { if (newImplementation.code.length == 0) { revert ERC1967InvalidImplementation(newImplementation); } StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation; } /** * @dev Performs implementation upgrade with additional setup call if data is nonempty. * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected * to avoid stuck value in the contract. * * Emits an {IERC1967-Upgraded} event. */ function upgradeToAndCall(address newImplementation, bytes memory data) internal { _setImplementation(newImplementation); emit IERC1967.Upgraded(newImplementation); if (data.length > 0) { Address.functionDelegateCall(newImplementation, data); } else { _checkNonPayable(); } } /** * @dev Storage slot with the admin of the contract. * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1. */ // solhint-disable-next-line private-vars-leading-underscore bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /** * @dev Returns the current admin. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` */ function getAdmin() internal view returns (address) { return StorageSlot.getAddressSlot(ADMIN_SLOT).value; } /** * @dev Stores a new address in the ERC-1967 admin slot. */ function _setAdmin(address newAdmin) private { if (newAdmin == address(0)) { revert ERC1967InvalidAdmin(address(0)); } StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin; } /** * @dev Changes the admin of the proxy. * * Emits an {IERC1967-AdminChanged} event. */ function changeAdmin(address newAdmin) internal { emit IERC1967.AdminChanged(getAdmin(), newAdmin); _setAdmin(newAdmin); } /** * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1. */ // solhint-disable-next-line private-vars-leading-underscore bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; /** * @dev Returns the current beacon. */ function getBeacon() internal view returns (address) { return StorageSlot.getAddressSlot(BEACON_SLOT).value; } /** * @dev Stores a new beacon in the ERC-1967 beacon slot. */ function _setBeacon(address newBeacon) private { if (newBeacon.code.length == 0) { revert ERC1967InvalidBeacon(newBeacon); } StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon; address beaconImplementation = IBeacon(newBeacon).implementation(); if (beaconImplementation.code.length == 0) { revert ERC1967InvalidImplementation(beaconImplementation); } } /** * @dev Change the beacon and trigger a setup call if data is nonempty. * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected * to avoid stuck value in the contract. * * Emits an {IERC1967-BeaconUpgraded} event. * * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for * efficiency. */ function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal { _setBeacon(newBeacon); emit IERC1967.BeaconUpgraded(newBeacon); if (data.length > 0) { Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); } else { _checkNonPayable(); } } /** * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract * if an upgrade doesn't perform an initialization call. */ function _checkNonPayable() private { if (msg.value > 0) { revert ERC1967NonPayable(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.20; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Storage of the initializable contract. * * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions * when using with upgradeable contracts. * * @custom:storage-location erc7201:openzeppelin.storage.Initializable */ struct InitializableStorage { /** * @dev Indicates that the contract has been initialized. */ uint64 _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool _initializing; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; /** * @dev The contract is already initialized. */ error InvalidInitialization(); /** * @dev The contract is not initializing. */ error NotInitializing(); /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint64 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in * production. * * Emits an {Initialized} event. */ modifier initializer() { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); // Cache values to avoid duplicated sloads bool isTopLevelCall = !$._initializing; uint64 initialized = $._initialized; // Allowed calls: // - initialSetup: the contract is not in the initializing state and no previous version was // initialized // - construction: the contract is initialized at version 1 (no reinitialization) and the // current contract is just being deployed bool initialSetup = initialized == 0 && isTopLevelCall; bool construction = initialized == 1 && address(this).code.length == 0; if (!initialSetup && !construction) { revert InvalidInitialization(); } $._initialized = 1; if (isTopLevelCall) { $._initializing = true; } _; if (isTopLevelCall) { $._initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint64 version) { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing || $._initialized >= version) { revert InvalidInitialization(); } $._initialized = version; $._initializing = true; _; $._initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { _checkInitializing(); _; } /** * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. */ function _checkInitializing() internal view virtual { if (!_isInitializing()) { revert NotInitializing(); } } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing) { revert InvalidInitialization(); } if ($._initialized != type(uint64).max) { $._initialized = type(uint64).max; emit Initialized(type(uint64).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint64) { return _getInitializableStorage()._initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _getInitializableStorage()._initializing; } /** * @dev Pointer to storage slot. Allows integrators to override it with a custom storage location. * * NOTE: Consider following the ERC-7201 formula to derive storage locations. */ function _initializableStorageSlot() internal pure virtual returns (bytes32) { return INITIALIZABLE_STORAGE; } /** * @dev Returns a pointer to the storage namespace. */ // solhint-disable-next-line var-name-mixedcase function _getInitializableStorage() private pure returns (InitializableStorage storage $) { bytes32 slot = _initializableStorageSlot(); assembly { $.slot := slot } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[ERC]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol) pragma solidity ^0.8.20; /** * @dev This is the interface that {BeaconProxy} expects of its beacon. */ interface IBeacon { /** * @dev Must return an address that can be used as a delegate call target. * * {UpgradeableBeacon} will check that this address is a contract. */ function implementation() external view returns (address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol) pragma solidity ^0.8.20; /** * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC. */ interface IERC1967 { /** * @dev Emitted when the implementation is upgraded. */ event Upgraded(address indexed implementation); /** * @dev Emitted when the admin account has changed. */ event AdminChanged(address previousAdmin, address newAdmin); /** * @dev Emitted when the beacon is changed. */ event BeaconUpgraded(address indexed beacon); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.2.0) (utils/Address.sol) pragma solidity ^0.8.20; import {Errors} from "./Errors.sol"; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @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://consensys.net/diligence/blog/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.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert Errors.InsufficientBalance(address(this).balance, amount); } (bool success, bytes memory returndata) = recipient.call{value: amount}(""); if (!success) { _revert(returndata); } } /** * @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 or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {Errors.FailedCall} error. * * 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. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @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`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert Errors.InsufficientBalance(address(this).balance, value); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case * of an unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {Errors.FailedCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}. */ function _revert(bytes memory returndata) 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 assembly ("memory-safe") { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert Errors.FailedCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol) // This file was procedurally generated from scripts/generate/templates/StorageSlot.js. pragma solidity ^0.8.20; /** * @dev Library for reading and writing primitive types to specific storage slots. * * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. * This library helps with reading and writing to such slots without the need for inline assembly. * * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. * * Example usage to set ERC-1967 implementation slot: * ```solidity * contract ERC1967 { * // Define the slot. Alternatively, use the SlotDerivation library to derive the slot. * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; * * function _getImplementation() internal view returns (address) { * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; * } * * function _setImplementation(address newImplementation) internal { * require(newImplementation.code.length > 0); * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; * } * } * ``` * * TIP: Consider using this library along with {SlotDerivation}. */ library StorageSlot { struct AddressSlot { address value; } struct BooleanSlot { bool value; } struct Bytes32Slot { bytes32 value; } struct Uint256Slot { uint256 value; } struct Int256Slot { int256 value; } struct StringSlot { string value; } struct BytesSlot { bytes value; } /** * @dev Returns an `AddressSlot` with member `value` located at `slot`. */ function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `BooleanSlot` with member `value` located at `slot`. */ function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `Bytes32Slot` with member `value` located at `slot`. */ function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `Uint256Slot` with member `value` located at `slot`. */ function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `Int256Slot` with member `value` located at `slot`. */ function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `StringSlot` with member `value` located at `slot`. */ function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns an `StringSlot` representation of the string storage pointer `store`. */ function getStringSlot(string storage store) internal pure returns (StringSlot storage r) { assembly ("memory-safe") { r.slot := store.slot } } /** * @dev Returns a `BytesSlot` with member `value` located at `slot`. */ function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`. */ function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) { assembly ("memory-safe") { r.slot := store.slot } } }
{ "remappings": [ "@axelar-network/=lib/axelar-gmp-sdk-solidity/", "forge-std/=lib/forge-std/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/", "@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/", "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "axelar-gmp-sdk-solidity/=lib/axelar-gmp-sdk-solidity/contracts/", "ds-test/=lib/solmate/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", "halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/", "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", "solmate/=lib/solmate/src/" ], "optimizer": { "enabled": true, "runs": 999999 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "london", "viaIR": true, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract PayIntentFactory","name":"_intentFactory","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"intentAddr","type":"address"},{"indexed":true,"internalType":"address","name":"finalRecipient","type":"address"}],"name":"Claim","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"intentAddr","type":"address"},{"indexed":true,"internalType":"address","name":"newRecipient","type":"address"}],"name":"FastFinish","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"intentAddr","type":"address"},{"indexed":true,"internalType":"address","name":"destinationAddr","type":"address"},{"indexed":true,"internalType":"bool","name":"success","type":"bool"},{"components":[{"internalType":"uint256","name":"toChainId","type":"uint256"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"bridgeTokenOutOptions","type":"tuple[]"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount","name":"finalCallToken","type":"tuple"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Call","name":"finalCall","type":"tuple"},{"internalType":"address payable","name":"escrow","type":"address"},{"internalType":"contract IDaimoPayBridger","name":"bridger","type":"address"},{"internalType":"address","name":"refundAddress","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expirationTimestamp","type":"uint256"}],"indexed":false,"internalType":"struct PayIntent","name":"intent","type":"tuple"}],"name":"IntentFinished","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"intentAddr","type":"address"},{"indexed":true,"internalType":"address","name":"refundAddr","type":"address"},{"indexed":false,"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"components":[{"internalType":"uint256","name":"toChainId","type":"uint256"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"bridgeTokenOutOptions","type":"tuple[]"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount","name":"finalCallToken","type":"tuple"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Call","name":"finalCall","type":"tuple"},{"internalType":"address payable","name":"escrow","type":"address"},{"internalType":"contract IDaimoPayBridger","name":"bridger","type":"address"},{"internalType":"address","name":"refundAddress","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expirationTimestamp","type":"uint256"}],"indexed":false,"internalType":"struct PayIntent","name":"intent","type":"tuple"}],"name":"IntentRefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"intentAddr","type":"address"},{"components":[{"internalType":"uint256","name":"toChainId","type":"uint256"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"bridgeTokenOutOptions","type":"tuple[]"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount","name":"finalCallToken","type":"tuple"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Call","name":"finalCall","type":"tuple"},{"internalType":"address payable","name":"escrow","type":"address"},{"internalType":"contract IDaimoPayBridger","name":"bridger","type":"address"},{"internalType":"address","name":"refundAddress","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expirationTimestamp","type":"uint256"}],"indexed":false,"internalType":"struct PayIntent","name":"intent","type":"tuple"}],"name":"Start","type":"event"},{"inputs":[{"components":[{"internalType":"uint256","name":"toChainId","type":"uint256"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"bridgeTokenOutOptions","type":"tuple[]"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount","name":"finalCallToken","type":"tuple"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Call","name":"finalCall","type":"tuple"},{"internalType":"address payable","name":"escrow","type":"address"},{"internalType":"contract IDaimoPayBridger","name":"bridger","type":"address"},{"internalType":"address","name":"refundAddress","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expirationTimestamp","type":"uint256"}],"internalType":"struct PayIntent","name":"intent","type":"tuple"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Call[]","name":"calls","type":"tuple[]"}],"name":"claimIntent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"executor","outputs":[{"internalType":"contract DaimoPayExecutor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"toChainId","type":"uint256"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"bridgeTokenOutOptions","type":"tuple[]"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount","name":"finalCallToken","type":"tuple"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Call","name":"finalCall","type":"tuple"},{"internalType":"address payable","name":"escrow","type":"address"},{"internalType":"contract IDaimoPayBridger","name":"bridger","type":"address"},{"internalType":"address","name":"refundAddress","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expirationTimestamp","type":"uint256"}],"internalType":"struct PayIntent","name":"intent","type":"tuple"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Call[]","name":"calls","type":"tuple[]"},{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"}],"name":"fastFinishIntent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"intentFactory","outputs":[{"internalType":"contract PayIntentFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"intentAddr","type":"address"}],"name":"intentSent","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"intentAddr","type":"address"}],"name":"intentToRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"toChainId","type":"uint256"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"bridgeTokenOutOptions","type":"tuple[]"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount","name":"finalCallToken","type":"tuple"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Call","name":"finalCall","type":"tuple"},{"internalType":"address payable","name":"escrow","type":"address"},{"internalType":"contract IDaimoPayBridger","name":"bridger","type":"address"},{"internalType":"address","name":"refundAddress","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expirationTimestamp","type":"uint256"}],"internalType":"struct PayIntent","name":"intent","type":"tuple"},{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"}],"name":"refundIntent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"toChainId","type":"uint256"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"bridgeTokenOutOptions","type":"tuple[]"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount","name":"finalCallToken","type":"tuple"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Call","name":"finalCall","type":"tuple"},{"internalType":"address payable","name":"escrow","type":"address"},{"internalType":"contract IDaimoPayBridger","name":"bridger","type":"address"},{"internalType":"address","name":"refundAddress","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expirationTimestamp","type":"uint256"}],"internalType":"struct PayIntent","name":"intent","type":"tuple"},{"internalType":"contract IERC20[]","name":"paymentTokens","type":"address[]"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Call[]","name":"calls","type":"tuple[]"},{"internalType":"bytes","name":"bridgeExtraData","type":"bytes"}],"name":"startIntent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60c03461010957601f61326538819003918201601f19168301916001600160401b038311848410176100f35780849260209460405283398101031261010957516001600160a01b0381168103610109576001600055608052604051610bf0808201906001600160401b038211838310176100f357602091839161267583393081520301906000f080156100e75760a052604051612566908161010f823960805181818160f0015281816104d20152818161065801528181610dcd015261122c015260a0518181816104630152818161073301528181610f19015281816112d40152611e960152f35b6040513d6000823e3d90fd5b634e487b7160e01b600052604160045260246000fd5b600080fdfe6080604052600436101561001b575b361561001957600080fd5b005b6000803560e01c80630bbc44c3146110f657806310e5317b1461108e57806313f167a514610d415780633fc38ffe14610cdc578063775ece72146104f6578063aac6b1fe14610487578063c34c08e5146104185763e03096081461007f575061000e565b346104155761008d366114a1565b610098929192611dcd565b6040517f73f94cb800000000000000000000000000000000000000000000000000000000815260206004820152602081806100d660248201876117d8565b03818873ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1801561040a5773ffffffffffffffffffffffffffffffffffffffff9186916103db575b5016916101208101354210158135460361034e57838652600260205273ffffffffffffffffffffffffffffffffffffffff8060408820541614908115610346575b50156102e8576101db905b60e081018673ffffffffffffffffffffffffffffffffffffffff6101a483611935565b16604051809581927fb9a815db000000000000000000000000000000000000000000000000000000008352888b8860048601611bce565b038183895af19283156102dd57879361029b575b5073ffffffffffffffffffffffffffffffffffffffff61021161022592611935565b169560405194606086526060860191611b7f565b838103602085015260208084519283815201930190875b818110610285575050509161027b827f14046046264ddf75828fe89d5e5f433eabda97ef2f76857eded9aacd77c66374948380950360408501526117d8565b0390a36001815580f35b825185526020948501949092019160010161023c565b6102259193506102116102d473ffffffffffffffffffffffffffffffffffffffff923d808c833e6102cc8183611647565b810190611b05565b949250506101ef565b6040513d89823e3d90fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f44503a206e6f7420636c61696d656400000000000000000000000000000000006044820152fd5b905038610176565b838652600160205260ff6040872054169081156103d3575b5015610375576101db90610181565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f44503a206e6f74207374617274656400000000000000000000000000000000006044820152fd5b905038610366565b6103fd915060203d602011610403575b6103f58183611647565b810190611956565b38610135565b503d6103eb565b6040513d87823e3d90fd5b80fd5b503461041557807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261041557602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461041557807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261041557602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346104155760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104155760043567ffffffffffffffff8111610cd857806004016101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83360301126108b35760243567ffffffffffffffff8111610cd457610589903690600401611427565b60443567ffffffffffffffff81116108c2576105a9903690600401611427565b6064359567ffffffffffffffff8711610bcc5736602388011215610bcc5786600401359667ffffffffffffffff8811610c30573660248983010111610c30576105f0611dcd565b6106006101248301354210611597565b6040517f73f94cb8000000000000000000000000000000000000000000000000000000008152602060048201526020818061063e602482018c6117d8565b03818d73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af18015610c255773ffffffffffffffffffffffffffffffffffffffff918b91610cb5575b501697888a52600160205260ff60408b205416610c57578996878a9788825260026020526106e773ffffffffffffffffffffffffffffffffffffffff806040852054161415611982565b88825260016020526040822060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905573ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016988993838d610791604051978896879586947fb9a815db00000000000000000000000000000000000000000000000000000000865260048601611bce565b03925af18015610c4c57610c34575b508735904682036108c65750505060246107bb9101866119e7565b939092813b156108c2578561080d9361083d8296604051988997889687957ff1777410000000000000000000000000000000000000000000000000000000008752608060048801526084870191611cb7565b917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc858403016024860152611a3b565b8a604483015233606483015203925af180156108b75761089e575b50506108947fb3270f5ab64f518be5ce0779de1a2fda680a18bba27720b67f271b531331f332915b6040519182916020835260208301906117d8565b0390a26001815580f35b816108a891611647565b6108b3578238610858565b8280fd5b6040513d84823e3d90fd5b8580fd5b93919560c48496929601359373ffffffffffffffffffffffffffffffffffffffff8516809503610c3057602401916109436040610903858d6119e7565b825193849283927ff80e0a420000000000000000000000000000000000000000000000000000000084528d60048501528560248501526044840191611a3b565b0381895afa948515610c25578a918b96610bdf575b5073ffffffffffffffffffffffffffffffffffffffff610976611c0e565b921693604051610985816115fc565b85815287602082015261099784611c7b565b526109a183611c7b565b50803b15610bdb576109ea938c8094604051968795869485937ff17774100000000000000000000000000000000000000000000000000000000085523392309260048701611d34565b03925af1908115610bd0578991610bb7575b50506040517f095ea7b300000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff85166024820152604480820194909452928352610a7692610a5e606482611647565b610a68818361244f565b15610b5a575b5050876119e7565b929094823b15610b56578694610b0686928b946024610ad56040519b8c9a8b998a987f1c59e972000000000000000000000000000000000000000000000000000000008a5260048a015284890152608060448901526084880191611a3b565b927ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc86850301606487015201611707565b03925af180156108b757610b41575b50506108947fb3270f5ab64f518be5ce0779de1a2fda680a18bba27720b67f271b531331f33291610880565b81610b4b91611647565b6108b3578238610b15565b8680fd5b610bb091610bab6040517f095ea7b30000000000000000000000000000000000000000000000000000000060208201528760248201528b604482015260448152610ba5606482611647565b826124a5565b6124a5565b3880610a6e565b81610bc191611647565b610bcc5787386109fc565b8780fd5b6040513d8b823e3d90fd5b8b80fd5b915094506040813d604011610c1d575b81610bfc60409383611647565b81010312610c19576020610c0f82611688565b9101519438610958565b8980fd5b3d9150610bef565b6040513d8c823e3d90fd5b8880fd5b610c47903d808a833e6102cc8183611647565b6107a0565b6040513d8a823e3d90fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f44503a20616c72656164792073656e74000000000000000000000000000000006044820152fd5b610cce915060203d602011610403576103f58183611647565b3861069d565b8380fd5b5080fd5b50346104155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104155773ffffffffffffffffffffffffffffffffffffffff604060209282610d2f61145d565b16815260028452205416604051908152f35b503461041557610d50366114a1565b91610d59611dcd565b610d6546823514611532565b610d756101208201354210611597565b6040517f73f94cb80000000000000000000000000000000000000000000000000000000081526020600482015260208180610db360248201866117d8565b03818873ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1801561040a5773ffffffffffffffffffffffffffffffffffffffff91869161106f575b501692838552600260205273ffffffffffffffffffffffffffffffffffffffff604086205416610e5873ffffffffffffffffffffffffffffffffffffffff821415611982565b84865260026020526040862080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff17905580610fbe5750610ebb610eb66080840184611aba565b611935565b9185610eca60208301836119e7565b873b156108b357610f4491839160405193849283927ff572544200000000000000000000000000000000000000000000000000000000845273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016918960048601611a73565b0381838b5af180156108b757610fa5575b50509073ffffffffffffffffffffffffffffffffffffffff93610f789286611e08565b16907f1836092b86c602f5dc00f47313b2873163879c06590285c6c58d63e208ac74668380a36001815580f35b81610fb291949394611647565b6108c257908538610f55565b92505083610fcf60208301836119e7565b92853b156108b35761100f8560405195869485947ff572544200000000000000000000000000000000000000000000000000000000865260048601611a73565b038183875af180156110645761103c575b5073ffffffffffffffffffffffffffffffffffffffff90610f78565b9261105d8173ffffffffffffffffffffffffffffffffffffffff9395611647565b9290611020565b6040513d86823e3d90fd5b611088915060203d602011610403576103f58183611647565b38610e12565b50346104155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104155760ff604060209273ffffffffffffffffffffffffffffffffffffffff6110e261145d565b168152600184522054166040519015158152f35b50346104155760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104155760043567ffffffffffffffff8111610cd85780600401906101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82360301126108b35760243567ffffffffffffffff8111610cd45761118a903690600401611427565b9190926044359267ffffffffffffffff84116108c2576111d46111b4610124953690600401611427565b9590946111bf611dcd565b6111cb46863514611532565b01354210611597565b604051947ffb24db75000000000000000000000000000000000000000000000000000000008652602060048701526020868061121360248201876117d8565b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9586156102dd5787966113eb575b5073ffffffffffffffffffffffffffffffffffffffff861692838852600260205273ffffffffffffffffffffffffffffffffffffffff60408920541661138d5783885260026020526040882080547fffffffffffffffffffffffff0000000000000000000000000000000000000000163317905594957f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169590885b88811015611351576001906113268160051b8901611935565b6113303082612165565b8a81611340575b5050500161130d565b6113499261227e565b38808a611337565b50889650611360949550611e08565b33907fdabac274ef42c98dfa48094f74ec810664a0a7a93735e365be0cae284c2d98078380a36001815580f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f44503a20616c72656164792066696e69736865640000000000000000000000006044820152fd5b9095506020813d60201161141f575b8161140760209383611647565b81010312610b565761141890611688565b943861125c565b3d91506113fa565b9181601f840112156114585782359167ffffffffffffffff8311611458576020808501948460051b01011161145857565b600080fd5b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361145857565b359073ffffffffffffffffffffffffffffffffffffffff8216820361145857565b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126114585760043567ffffffffffffffff8111611458576101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc828503011261145857600401916024359067ffffffffffffffff82116114585761152e91600401611427565b9091565b1561153957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f44503a2077726f6e6720636861696e00000000000000000000000000000000006044820152fd5b1561159e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f44503a20696e74656e74206578706972656400000000000000000000000000006044820152fd5b6040810190811067ffffffffffffffff82111761161857604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761161857604052565b519073ffffffffffffffffffffffffffffffffffffffff8216820361145857565b6020809173ffffffffffffffffffffffffffffffffffffffff6116cb82611480565b1684520135910152565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa182360301811215611458570190565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b73ffffffffffffffffffffffffffffffffffffffff61176482611480565b1682526020810135602083015260408101357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561145857016020813591019067ffffffffffffffff8111611458578036038213611458576060838160406117d59601520191611707565b90565b9061014081018235825260208301357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112156114585783016020813591019167ffffffffffffffff8211611458578160061b3603831361145857610140602085015281905261016083019060005b818110611915575050611883915061186960408401604086016116a9565b61187660808501856116d5565b8382036080850152611746565b9160a081013573ffffffffffffffffffffffffffffffffffffffff81168091036114585760a083015260c081013573ffffffffffffffffffffffffffffffffffffffff81168091036114585761012091829160c085015273ffffffffffffffffffffffffffffffffffffffff6118fb60e08301611480565b1660e0850152610100810135610100850152013591015290565b909160408082611927600194886116a9565b01940191019291909261184b565b3573ffffffffffffffffffffffffffffffffffffffff811681036114585790565b90816020910312611458575173ffffffffffffffffffffffffffffffffffffffff811681036114585790565b1561198957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f44503a20616c726561647920636c61696d6564000000000000000000000000006044820152fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215611458570180359067ffffffffffffffff821161145857602001918160061b3603831361145857565b9160209082815201919060005b818110611a555750505090565b90919260408082611a68600194886116a9565b019401929101611a48565b92604092611ab391611aa573ffffffffffffffffffffffffffffffffffffffff949897986060885260608801906117d8565b918683036020880152611a3b565b9416910152565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa181360301821215611458570190565b67ffffffffffffffff81116116185760051b60200190565b6020818303126114585780519067ffffffffffffffff821161145857019080601f83011215611458578151611b3981611aed565b92611b476040519485611647565b81845260208085019260051b82010192831161145857602001905b828210611b6f5750505090565b8151815260209182019101611b62565b9160209082815201919060005b818110611b995750505090565b90919260208060019273ffffffffffffffffffffffffffffffffffffffff611bc088611480565b168152019401929101611b8c565b92604092611ab391611c0073ffffffffffffffffffffffffffffffffffffffff949897986060885260608801906117d8565b918683036020880152611b7f565b60408051909190611c1f8382611647565b60018152917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018260005b828110611c5657505050565b602090604051611c65816115fc565b6000815260008382015282828501015201611c4a565b805115611c885760200190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90602083828152019260208260051b82010193836000925b848410611cdf5750505050505090565b909192939495602080611d24837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08660019603018852611d1f8b886116d5565b611746565b9801940194019294939190611ccf565b939594929190611d4c91608086526080860191611cb7565b83810360208501526020808351928381520192019060005b818110611d955750505060609173ffffffffffffffffffffffffffffffffffffffff80929616604085015216910152565b8251805173ffffffffffffffffffffffffffffffffffffffff1685526020908101518186015260409094019390920191600101611d64565b600260005414611dde576002600055565b7f3ee5aeb50000000000000000000000000000000000000000000000000000000060005260046000fd5b9192611e12611c0e565b90604083019060009060407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08636030112610cd85760405196611e54886115fc565b611e5d84611480565b8852606086013597886020820152611e7486611c7b565b52611e7e85611c7b565b5073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001694853b15610cd457908391611efc60405194859384937ff17774100000000000000000000000000000000000000000000000000000000085523392309260048701611d34565b038183885af180156108b7578290612155575b50506080840192611f208486611aba565b6040810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610cd4570180359067ffffffffffffffff8211610cd45760208236039101136108b357156120ee576020611fc48392611f878789611aba565b6040519586809481937fc51d5d96000000000000000000000000000000000000000000000000000000008352606060048401526064830190611746565b611fd1602483018b6116a9565b03925af19182156120e1578192612080575b5050610eb67ff99f9cd5a088d5dac6f346c97d15369b824d0e8b5438ffcec0109e2ef554a1569373ffffffffffffffffffffffffffffffffffffffff93612035935b159889612059575b505085611aba565b94612054826040519384936020855215981696169460208301906117d8565b0390a4565b61206561207992611935565b8661207260e08b01611935565b169061227e565b388061202d565b9091506020813d6020116120d9575b8161209c60209383611647565b81010312610cd857519081151582036104155750610eb67ff99f9cd5a088d5dac6f346c97d15369b824d0e8b5438ffcec0109e2ef554a156611fe3565b3d915061208f565b50604051903d90823e3d90fd5b5050612035610eb67ff99f9cd5a088d5dac6f346c97d15369b824d0e8b5438ffcec0109e2ef554a15693836121508961213b73ffffffffffffffffffffffffffffffffffffffff97611935565b87612149610eb6878d611aba565b16906123b4565b612025565b61215e91611647565b3881611f0f565b73ffffffffffffffffffffffffffffffffffffffff168061218557503190565b9073ffffffffffffffffffffffffffffffffffffffff602460209260405194859384927f70a082310000000000000000000000000000000000000000000000000000000084521660048301525afa908115612214576000916121e5575090565b90506020813d60201161220c575b8161220060209383611647565b81010312611458575190565b3d91506121f3565b6040513d6000823e3d90fd5b3d15612279573d9067ffffffffffffffff8211611618576040519161226d60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184611647565b82523d6000602084013e565b606090565b9073ffffffffffffffffffffffffffffffffffffffff821615612325576040517fa9059cbb00000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff909116602482015260448101929092526123239190610bab82606481015b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101845283611647565b565b6000809381935073ffffffffffffffffffffffffffffffffffffffff8293165af161234e612220565b501561235657565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f546f6b656e5574696c733a20455448207472616e73666572206661696c6564006044820152fd5b90919073ffffffffffffffffffffffffffffffffffffffff811615612438576040517fa9059cbb00000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff909316602484015260448301919091526117d5919061243382606481016122f7565b61244f565b5060008080939281935af161244b612220565b5090565b906000602091828151910182855af1903d6000519083612470575b50505090565b9192509061249b575073ffffffffffffffffffffffffffffffffffffffff163b15155b38808061246a565b6001915014612493565b906000602091828151910182855af115612214576000513d612527575073ffffffffffffffffffffffffffffffffffffffff81163b155b6124e35750565b73ffffffffffffffffffffffffffffffffffffffff907f5274afe7000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b600114156124dc56fea2646970667358221220f3793526af0d0044f50ae16220cc848964ff7652d7e0d1776558fc1bda212a0064736f6c634300081a003360a034607957601f610bf038819003918201601f19168301916001600160401b03831184841017607e57808492602094604052833981010312607957516001600160a01b03811681036079576001600055608052604051610b5b9081610095823960805181818161015f015281816103a101526104820152f35b600080fd5b634e487b7160e01b600052604160045260246000fdfe6080604052600436101561001b575b361561001957600080fd5b005b60003560e01c8063c51d5d96146103c5578063e2fdcc17146103565763f17774100361000e57346103515760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103515760043567ffffffffffffffff811161035157366023820112156103515780600401359067ffffffffffffffff8211610351573660248360051b83010111610351576024359167ffffffffffffffff831161035157366023840112156103515782600401359267ffffffffffffffff8411610351576024810190602436918660061b010111610351576044359073ffffffffffffffffffffffffffffffffffffffff82168203610351576064359173ffffffffffffffffffffffffffffffffffffffff831683036103515761014595939561084e565b61018673ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016331461066a565b6000937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7d86360301945b8781101561027c57600060248260051b890101358781121561027857819089016044602482016101ed6101e2826106cf565b9160648501906106f0565b9290836040519485928337810186815203930135905af161020c6107b1565b501561021a576001016101b0565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f445043453a2063616c6c206661696c65640000000000000000000000000000006044820152fd5b5080fd5b506102878184610889565b90808210156102f357836102c46102a9946102ae6102a986866102c99a61080f565b6106cf565b9060206102bc87878761080f565b0135916108ce565b61080f565b906102d430836109df565b90816102e2575b6001600055005b6102eb926108ce565b8080806102db565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f445043453a20696e73756666696369656e74206f7574707574000000000000006044820152fd5b600080fd5b346103515760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035157602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346103515760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103515760043567ffffffffffffffff8111610351578060040160607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83360301126103515760407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601126103515761046861084e565b6104a973ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016331461066a565b60243573ffffffffffffffffffffffffffffffffffffffff8116928382036103515760209360009384936024926104df836106cf565b918161052f575b5050506105006104f5826106cf565b9160448501906106f0565b9290836040519485928337810186815203930135905af161051f6107b1565b5060016000556040519015158152f35b6040519189888185017f095ea7b30000000000000000000000000000000000000000000000000000000081526105bd866105916044358a8d84016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101885287610741565b85519082865af1903d89519083610648575b5050506104e65761063b6106409373ffffffffffffffffffffffffffffffffffffffff604051917f095ea7b3000000000000000000000000000000000000000000000000000000008d840152168782015288604482015260448152610635606482610741565b82610a9a565b610a9a565b8680806104e6565b9091925015891461066057503b15155b8a80806105cf565b6001915014610658565b1561067157565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f445043453a206f6e6c7920657363726f770000000000000000000000000000006044820152fd5b3573ffffffffffffffffffffffffffffffffffffffff811681036103515790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610351570180359067ffffffffffffffff82116103515760200191813603831361035157565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761078257604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b3d1561080a573d9067ffffffffffffffff821161078257604051916107fe60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610741565b82523d6000602084013e565b606090565b919081101561081f5760061b0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60026000541461085f576002600055565b7f3ee5aeb50000000000000000000000000000000000000000000000000000000060005260046000fd5b60005b82811061089857505090565b6108a381848461080f565b60206108b7306108b2846106cf565b6109df565b91013511156108c85760010161088c565b91505090565b9073ffffffffffffffffffffffffffffffffffffffff821615610950576040517fa9059cbb00000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff91909116602482015260448082019390935291825261094e919061063b606483610741565b565b6000809381935073ffffffffffffffffffffffffffffffffffffffff8293165af16109796107b1565b501561098157565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f546f6b656e5574696c733a20455448207472616e73666572206661696c6564006044820152fd5b73ffffffffffffffffffffffffffffffffffffffff16806109ff57503190565b9073ffffffffffffffffffffffffffffffffffffffff602460209260405194859384927f70a082310000000000000000000000000000000000000000000000000000000084521660048301525afa908115610a8e57600091610a5f575090565b90506020813d602011610a86575b81610a7a60209383610741565b81010312610351575190565b3d9150610a6d565b6040513d6000823e3d90fd5b906000602091828151910182855af115610a8e576000513d610b1c575073ffffffffffffffffffffffffffffffffffffffff81163b155b610ad85750565b73ffffffffffffffffffffffffffffffffffffffff907f5274afe7000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b60011415610ad156fea2646970667358221220cdcaf47f858b386c009331fc1b40c8ed77c0d1cba13df424c216899fbd6e9fe664736f6c634300081a00330000000000000000000000009751572b505f244d9fecfcb9712b00974f458872000000000000000000000000ea3f5a3695a85f465071873e7de62f104598b93d
Deployed Bytecode
0x6080604052600436101561001b575b361561001957600080fd5b005b6000803560e01c80630bbc44c3146110f657806310e5317b1461108e57806313f167a514610d415780633fc38ffe14610cdc578063775ece72146104f6578063aac6b1fe14610487578063c34c08e5146104185763e03096081461007f575061000e565b346104155761008d366114a1565b610098929192611dcd565b6040517f73f94cb800000000000000000000000000000000000000000000000000000000815260206004820152602081806100d660248201876117d8565b03818873ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000009751572b505f244d9fecfcb9712b00974f458872165af1801561040a5773ffffffffffffffffffffffffffffffffffffffff9186916103db575b5016916101208101354210158135460361034e57838652600260205273ffffffffffffffffffffffffffffffffffffffff8060408820541614908115610346575b50156102e8576101db905b60e081018673ffffffffffffffffffffffffffffffffffffffff6101a483611935565b16604051809581927fb9a815db000000000000000000000000000000000000000000000000000000008352888b8860048601611bce565b038183895af19283156102dd57879361029b575b5073ffffffffffffffffffffffffffffffffffffffff61021161022592611935565b169560405194606086526060860191611b7f565b838103602085015260208084519283815201930190875b818110610285575050509161027b827f14046046264ddf75828fe89d5e5f433eabda97ef2f76857eded9aacd77c66374948380950360408501526117d8565b0390a36001815580f35b825185526020948501949092019160010161023c565b6102259193506102116102d473ffffffffffffffffffffffffffffffffffffffff923d808c833e6102cc8183611647565b810190611b05565b949250506101ef565b6040513d89823e3d90fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f44503a206e6f7420636c61696d656400000000000000000000000000000000006044820152fd5b905038610176565b838652600160205260ff6040872054169081156103d3575b5015610375576101db90610181565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f44503a206e6f74207374617274656400000000000000000000000000000000006044820152fd5b905038610366565b6103fd915060203d602011610403575b6103f58183611647565b810190611956565b38610135565b503d6103eb565b6040513d87823e3d90fd5b80fd5b503461041557807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261041557602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000339bbabbf84f47ddb0f49473168d8ea8030b847c168152f35b503461041557807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261041557602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000009751572b505f244d9fecfcb9712b00974f458872168152f35b50346104155760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104155760043567ffffffffffffffff8111610cd857806004016101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83360301126108b35760243567ffffffffffffffff8111610cd457610589903690600401611427565b60443567ffffffffffffffff81116108c2576105a9903690600401611427565b6064359567ffffffffffffffff8711610bcc5736602388011215610bcc5786600401359667ffffffffffffffff8811610c30573660248983010111610c30576105f0611dcd565b6106006101248301354210611597565b6040517f73f94cb8000000000000000000000000000000000000000000000000000000008152602060048201526020818061063e602482018c6117d8565b03818d73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000009751572b505f244d9fecfcb9712b00974f458872165af18015610c255773ffffffffffffffffffffffffffffffffffffffff918b91610cb5575b501697888a52600160205260ff60408b205416610c57578996878a9788825260026020526106e773ffffffffffffffffffffffffffffffffffffffff806040852054161415611982565b88825260016020526040822060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905573ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000339bbabbf84f47ddb0f49473168d8ea8030b847c16988993838d610791604051978896879586947fb9a815db00000000000000000000000000000000000000000000000000000000865260048601611bce565b03925af18015610c4c57610c34575b508735904682036108c65750505060246107bb9101866119e7565b939092813b156108c2578561080d9361083d8296604051988997889687957ff1777410000000000000000000000000000000000000000000000000000000008752608060048801526084870191611cb7565b917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc858403016024860152611a3b565b8a604483015233606483015203925af180156108b75761089e575b50506108947fb3270f5ab64f518be5ce0779de1a2fda680a18bba27720b67f271b531331f332915b6040519182916020835260208301906117d8565b0390a26001815580f35b816108a891611647565b6108b3578238610858565b8280fd5b6040513d84823e3d90fd5b8580fd5b93919560c48496929601359373ffffffffffffffffffffffffffffffffffffffff8516809503610c3057602401916109436040610903858d6119e7565b825193849283927ff80e0a420000000000000000000000000000000000000000000000000000000084528d60048501528560248501526044840191611a3b565b0381895afa948515610c25578a918b96610bdf575b5073ffffffffffffffffffffffffffffffffffffffff610976611c0e565b921693604051610985816115fc565b85815287602082015261099784611c7b565b526109a183611c7b565b50803b15610bdb576109ea938c8094604051968795869485937ff17774100000000000000000000000000000000000000000000000000000000085523392309260048701611d34565b03925af1908115610bd0578991610bb7575b50506040517f095ea7b300000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff85166024820152604480820194909452928352610a7692610a5e606482611647565b610a68818361244f565b15610b5a575b5050876119e7565b929094823b15610b56578694610b0686928b946024610ad56040519b8c9a8b998a987f1c59e972000000000000000000000000000000000000000000000000000000008a5260048a015284890152608060448901526084880191611a3b565b927ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc86850301606487015201611707565b03925af180156108b757610b41575b50506108947fb3270f5ab64f518be5ce0779de1a2fda680a18bba27720b67f271b531331f33291610880565b81610b4b91611647565b6108b3578238610b15565b8680fd5b610bb091610bab6040517f095ea7b30000000000000000000000000000000000000000000000000000000060208201528760248201528b604482015260448152610ba5606482611647565b826124a5565b6124a5565b3880610a6e565b81610bc191611647565b610bcc5787386109fc565b8780fd5b6040513d8b823e3d90fd5b8b80fd5b915094506040813d604011610c1d575b81610bfc60409383611647565b81010312610c19576020610c0f82611688565b9101519438610958565b8980fd5b3d9150610bef565b6040513d8c823e3d90fd5b8880fd5b610c47903d808a833e6102cc8183611647565b6107a0565b6040513d8a823e3d90fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f44503a20616c72656164792073656e74000000000000000000000000000000006044820152fd5b610cce915060203d602011610403576103f58183611647565b3861069d565b8380fd5b5080fd5b50346104155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104155773ffffffffffffffffffffffffffffffffffffffff604060209282610d2f61145d565b16815260028452205416604051908152f35b503461041557610d50366114a1565b91610d59611dcd565b610d6546823514611532565b610d756101208201354210611597565b6040517f73f94cb80000000000000000000000000000000000000000000000000000000081526020600482015260208180610db360248201866117d8565b03818873ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000009751572b505f244d9fecfcb9712b00974f458872165af1801561040a5773ffffffffffffffffffffffffffffffffffffffff91869161106f575b501692838552600260205273ffffffffffffffffffffffffffffffffffffffff604086205416610e5873ffffffffffffffffffffffffffffffffffffffff821415611982565b84865260026020526040862080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff17905580610fbe5750610ebb610eb66080840184611aba565b611935565b9185610eca60208301836119e7565b873b156108b357610f4491839160405193849283927ff572544200000000000000000000000000000000000000000000000000000000845273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000339bbabbf84f47ddb0f49473168d8ea8030b847c16918960048601611a73565b0381838b5af180156108b757610fa5575b50509073ffffffffffffffffffffffffffffffffffffffff93610f789286611e08565b16907f1836092b86c602f5dc00f47313b2873163879c06590285c6c58d63e208ac74668380a36001815580f35b81610fb291949394611647565b6108c257908538610f55565b92505083610fcf60208301836119e7565b92853b156108b35761100f8560405195869485947ff572544200000000000000000000000000000000000000000000000000000000865260048601611a73565b038183875af180156110645761103c575b5073ffffffffffffffffffffffffffffffffffffffff90610f78565b9261105d8173ffffffffffffffffffffffffffffffffffffffff9395611647565b9290611020565b6040513d86823e3d90fd5b611088915060203d602011610403576103f58183611647565b38610e12565b50346104155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104155760ff604060209273ffffffffffffffffffffffffffffffffffffffff6110e261145d565b168152600184522054166040519015158152f35b50346104155760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104155760043567ffffffffffffffff8111610cd85780600401906101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82360301126108b35760243567ffffffffffffffff8111610cd45761118a903690600401611427565b9190926044359267ffffffffffffffff84116108c2576111d46111b4610124953690600401611427565b9590946111bf611dcd565b6111cb46863514611532565b01354210611597565b604051947ffb24db75000000000000000000000000000000000000000000000000000000008652602060048701526020868061121360248201876117d8565b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000009751572b505f244d9fecfcb9712b00974f458872165afa9586156102dd5787966113eb575b5073ffffffffffffffffffffffffffffffffffffffff861692838852600260205273ffffffffffffffffffffffffffffffffffffffff60408920541661138d5783885260026020526040882080547fffffffffffffffffffffffff0000000000000000000000000000000000000000163317905594957f000000000000000000000000339bbabbf84f47ddb0f49473168d8ea8030b847c73ffffffffffffffffffffffffffffffffffffffff169590885b88811015611351576001906113268160051b8901611935565b6113303082612165565b8a81611340575b5050500161130d565b6113499261227e565b38808a611337565b50889650611360949550611e08565b33907fdabac274ef42c98dfa48094f74ec810664a0a7a93735e365be0cae284c2d98078380a36001815580f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f44503a20616c72656164792066696e69736865640000000000000000000000006044820152fd5b9095506020813d60201161141f575b8161140760209383611647565b81010312610b565761141890611688565b943861125c565b3d91506113fa565b9181601f840112156114585782359167ffffffffffffffff8311611458576020808501948460051b01011161145857565b600080fd5b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361145857565b359073ffffffffffffffffffffffffffffffffffffffff8216820361145857565b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126114585760043567ffffffffffffffff8111611458576101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc828503011261145857600401916024359067ffffffffffffffff82116114585761152e91600401611427565b9091565b1561153957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f44503a2077726f6e6720636861696e00000000000000000000000000000000006044820152fd5b1561159e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f44503a20696e74656e74206578706972656400000000000000000000000000006044820152fd5b6040810190811067ffffffffffffffff82111761161857604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761161857604052565b519073ffffffffffffffffffffffffffffffffffffffff8216820361145857565b6020809173ffffffffffffffffffffffffffffffffffffffff6116cb82611480565b1684520135910152565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa182360301811215611458570190565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b73ffffffffffffffffffffffffffffffffffffffff61176482611480565b1682526020810135602083015260408101357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561145857016020813591019067ffffffffffffffff8111611458578036038213611458576060838160406117d59601520191611707565b90565b9061014081018235825260208301357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112156114585783016020813591019167ffffffffffffffff8211611458578160061b3603831361145857610140602085015281905261016083019060005b818110611915575050611883915061186960408401604086016116a9565b61187660808501856116d5565b8382036080850152611746565b9160a081013573ffffffffffffffffffffffffffffffffffffffff81168091036114585760a083015260c081013573ffffffffffffffffffffffffffffffffffffffff81168091036114585761012091829160c085015273ffffffffffffffffffffffffffffffffffffffff6118fb60e08301611480565b1660e0850152610100810135610100850152013591015290565b909160408082611927600194886116a9565b01940191019291909261184b565b3573ffffffffffffffffffffffffffffffffffffffff811681036114585790565b90816020910312611458575173ffffffffffffffffffffffffffffffffffffffff811681036114585790565b1561198957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f44503a20616c726561647920636c61696d6564000000000000000000000000006044820152fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215611458570180359067ffffffffffffffff821161145857602001918160061b3603831361145857565b9160209082815201919060005b818110611a555750505090565b90919260408082611a68600194886116a9565b019401929101611a48565b92604092611ab391611aa573ffffffffffffffffffffffffffffffffffffffff949897986060885260608801906117d8565b918683036020880152611a3b565b9416910152565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa181360301821215611458570190565b67ffffffffffffffff81116116185760051b60200190565b6020818303126114585780519067ffffffffffffffff821161145857019080601f83011215611458578151611b3981611aed565b92611b476040519485611647565b81845260208085019260051b82010192831161145857602001905b828210611b6f5750505090565b8151815260209182019101611b62565b9160209082815201919060005b818110611b995750505090565b90919260208060019273ffffffffffffffffffffffffffffffffffffffff611bc088611480565b168152019401929101611b8c565b92604092611ab391611c0073ffffffffffffffffffffffffffffffffffffffff949897986060885260608801906117d8565b918683036020880152611b7f565b60408051909190611c1f8382611647565b60018152917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018260005b828110611c5657505050565b602090604051611c65816115fc565b6000815260008382015282828501015201611c4a565b805115611c885760200190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90602083828152019260208260051b82010193836000925b848410611cdf5750505050505090565b909192939495602080611d24837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08660019603018852611d1f8b886116d5565b611746565b9801940194019294939190611ccf565b939594929190611d4c91608086526080860191611cb7565b83810360208501526020808351928381520192019060005b818110611d955750505060609173ffffffffffffffffffffffffffffffffffffffff80929616604085015216910152565b8251805173ffffffffffffffffffffffffffffffffffffffff1685526020908101518186015260409094019390920191600101611d64565b600260005414611dde576002600055565b7f3ee5aeb50000000000000000000000000000000000000000000000000000000060005260046000fd5b9192611e12611c0e565b90604083019060009060407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08636030112610cd85760405196611e54886115fc565b611e5d84611480565b8852606086013597886020820152611e7486611c7b565b52611e7e85611c7b565b5073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000339bbabbf84f47ddb0f49473168d8ea8030b847c1694853b15610cd457908391611efc60405194859384937ff17774100000000000000000000000000000000000000000000000000000000085523392309260048701611d34565b038183885af180156108b7578290612155575b50506080840192611f208486611aba565b6040810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610cd4570180359067ffffffffffffffff8211610cd45760208236039101136108b357156120ee576020611fc48392611f878789611aba565b6040519586809481937fc51d5d96000000000000000000000000000000000000000000000000000000008352606060048401526064830190611746565b611fd1602483018b6116a9565b03925af19182156120e1578192612080575b5050610eb67ff99f9cd5a088d5dac6f346c97d15369b824d0e8b5438ffcec0109e2ef554a1569373ffffffffffffffffffffffffffffffffffffffff93612035935b159889612059575b505085611aba565b94612054826040519384936020855215981696169460208301906117d8565b0390a4565b61206561207992611935565b8661207260e08b01611935565b169061227e565b388061202d565b9091506020813d6020116120d9575b8161209c60209383611647565b81010312610cd857519081151582036104155750610eb67ff99f9cd5a088d5dac6f346c97d15369b824d0e8b5438ffcec0109e2ef554a156611fe3565b3d915061208f565b50604051903d90823e3d90fd5b5050612035610eb67ff99f9cd5a088d5dac6f346c97d15369b824d0e8b5438ffcec0109e2ef554a15693836121508961213b73ffffffffffffffffffffffffffffffffffffffff97611935565b87612149610eb6878d611aba565b16906123b4565b612025565b61215e91611647565b3881611f0f565b73ffffffffffffffffffffffffffffffffffffffff168061218557503190565b9073ffffffffffffffffffffffffffffffffffffffff602460209260405194859384927f70a082310000000000000000000000000000000000000000000000000000000084521660048301525afa908115612214576000916121e5575090565b90506020813d60201161220c575b8161220060209383611647565b81010312611458575190565b3d91506121f3565b6040513d6000823e3d90fd5b3d15612279573d9067ffffffffffffffff8211611618576040519161226d60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184611647565b82523d6000602084013e565b606090565b9073ffffffffffffffffffffffffffffffffffffffff821615612325576040517fa9059cbb00000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff909116602482015260448101929092526123239190610bab82606481015b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101845283611647565b565b6000809381935073ffffffffffffffffffffffffffffffffffffffff8293165af161234e612220565b501561235657565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f546f6b656e5574696c733a20455448207472616e73666572206661696c6564006044820152fd5b90919073ffffffffffffffffffffffffffffffffffffffff811615612438576040517fa9059cbb00000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff909316602484015260448301919091526117d5919061243382606481016122f7565b61244f565b5060008080939281935af161244b612220565b5090565b906000602091828151910182855af1903d6000519083612470575b50505090565b9192509061249b575073ffffffffffffffffffffffffffffffffffffffff163b15155b38808061246a565b6001915014612493565b906000602091828151910182855af115612214576000513d612527575073ffffffffffffffffffffffffffffffffffffffff81163b155b6124e35750565b73ffffffffffffffffffffffffffffffffffffffff907f5274afe7000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b600114156124dc56fea2646970667358221220f3793526af0d0044f50ae16220cc848964ff7652d7e0d1776558fc1bda212a0064736f6c634300081a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000009751572b505f244d9fecfcb9712b00974f458872000000000000000000000000ea3f5a3695a85f465071873e7de62f104598b93d
-----Decoded View---------------
Arg [0] : _intentFactory (address): 0x9751572B505F244D9feCfcB9712B00974f458872
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000009751572b505f244d9fecfcb9712b00974f458872
Arg [1] : 000000000000000000000000ea3f5a3695a85f465071873e7de62f104598b93d
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
Loading...
Loading
[ 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.