Address: 0x5CdC654bc149F72509442fa9c12762b269f34c23
Balance (XRP): 0 XRP
Bytecode: 0x60806040526004361015610011575f80fd5b5f3560e01c80634784226e146102e85780634c4e814c1461025e5780638dbf4cad146102455780639f795aac146101ff578063b8dc491b14610182578063d14f8dd714610169578063d275afd6146100a05763f8b2cb4f14610071575f80fd5b3461009c57602036600319011261009c57602061009461008f61054d565b610e0f565b604051908152f35b5f80fd5b6100a9366105c7565b9190307f0000000000000000000000005cdc654bc149f72509442fa9c12762b269f34c236001600160a01b03161461015a576100e6903083610b64565b506001600160a01b0381169082908261012a5750505f5160206110ab5f395f51905f52602047936101178582610fed565b6040519485526001600160a01b031693a3005b5f5160206110ab5f395f51905f529161011782610148602094610e81565b9681610155898094610ebb565b610fa3565b6327844c6960e11b5f5260045ffd5b3461009c57602061009461017c3661058d565b91610cc0565b604036600319011261009c5761019661054d565b61019e610563565b90307f0000000000000000000000005cdc654bc149f72509442fa9c12762b269f34c236001600160a01b03161461015a576001600160a01b0381169082908261012a5750505f5160206110ab5f395f51905f52602047936101178582610fed565b610208366105c7565b9190307f0000000000000000000000005cdc654bc149f72509442fa9c12762b269f34c236001600160a01b03161461015a576100e6903083610cc0565b3461009c5760206100946102583661058d565b91610b64565b3461009c5760c036600319011261009c5760a4356001600160401b03811161009c573660238201121561009c5780600401356001600160401b03811161009c57366024828401011161009c57307f0000000000000000000000005cdc654bc149f72509442fa9c12762b269f34c236001600160a01b03161461015a5760246102e69201610669565b005b608036600319011261009c576102fc61054d565b610304610563565b6064356001600160a01b0381169260443592909184810361009c57307f0000000000000000000000005cdc654bc149f72509442fa9c12762b269f34c236001600160a01b03161461015a576001600160a01b038316927fe8d24fc0ab3b12d83ce3d7bb06e74e2a423de5d1fa0d5414435460ede32ea6ea92908461045f57504780861115610454576103c490925b868403610427575b836103f2575b479081806103c9575b50506040516001600160a01b03909316969293849384610601565b0390a4005b6103d291610fed565b87865f5160206110ab5f395f51905f526020604051858152a388816103a9565b6103fc8484610fed565b6040518481526001600160a01b0384169087905f5160206110eb5f395f51905f5290602090a36103a0565b604051878152846020820152865f5160206110cb5f395f51905f52604060018060a01b03871693a361039a565b506103c48592610392565b916103c49061046d84610e81565b6104778186610ebb565b8088111561054557935b878503610518575b846104e2575b61049881610e81565b8092816104b8575b50505060405193849360018060a01b03169784610601565b6104c192610fa3565b87865f5160206110ab5f395f51905f526020604051858152a38881816104a0565b6104ed858583610fa3565b6040518581526001600160a01b0385169088905f5160206110eb5f395f51905f5290602090a361048f565b604051888152856020820152875f5160206110cb5f395f51905f52604060018060a01b03881693a3610489565b508693610481565b600435906001600160a01b038216820361009c57565b602435906001600160a01b038216820361009c57565b35906001600160a01b038216820361009c57565b606090600319011261009c576004356001600160a01b038116810361009c57906024356001600160a01b038116810361009c579060443590565b606090600319011261009c576004356001600160a01b038116810361009c5790602435906044356001600160a01b038116810361009c5790565b6040919493926060820195825260208201520152565b9081606091031261009c5761062b81610579565b9161063d604060208401359301610579565b90565b9081606091031261009c5761065481610579565b91604061066360208401610579565b92013590565b9190915f6004841015610aeb575b6001600160e01b0319169263b8dc491b60e01b8414610a5f576323c2113760e11b84146108625763693ad7eb60e11b8414610805576327de56ab60e21b841461075057638dbf4cad60e01b841461071e5763d14f8dd760e01b84146106e9578363fbdc730160e01b5f5260045260245ffd5b809192935060041161009c578160046107089261071b94019101610640565b916001600160a01b039182169116610cc0565b50565b809192935060041161009c5781600461073d9261071b94019101610640565b916001600160a01b039182169116610b64565b8092935060041161009c57600461076a9282019101610617565b6001600160a01b0390811692811691907f0000000000000000000000005cdc654bc149f72509442fa9c12762b269f34c2316301461015a576107ad903083610cc0565b50806107d7575f5160206110ab5f395f51905f526020476107ce8186610fed565b604051908152a3565b5f5160206110ab5f395f51905f5260206107f083610e81565b6107fa8185610ebb565b6107ce818686610fa3565b8092935060041161009c57600461081f9282019101610617565b6001600160a01b0390811692811691907f0000000000000000000000005cdc654bc149f72509442fa9c12762b269f34c2316301461015a576107ad903083610b64565b809192935060041161009c576080908201829003600319011261009c5761088b60048201610579565b61089760248301610579565b6108a8606460448501359401610579565b6001600160a01b039081169391811692811691907f0000000000000000000000005cdc654bc149f72509442fa9c12762b269f34c2316301461015a577fe8d24fc0ab3b12d83ce3d7bb06e74e2a423de5d1fa0d5414435460ede32ea6ea90826109b95747808211156109b257905b80820361098f575b81610967575b61093a478061093f575b60405193849384610601565b0390a4565b6109498189610fed565b87865f5160206110ab5f395f51905f526020604051858152a361092e565b6109718286610fed565b84845f5160206110eb5f395f51905f526020604051868152a3610924565b84845f5160206110cb5f395f51905f5260408051858152866020820152a361091e565b5080610916565b6109c283610e81565b6109cc8185610ebb565b80821115610a5857905b808203610a35575b81610a0c575b61093a6109f085610e81565b80610a015760405193849384610601565b610949818988610fa3565b610a17828686610fa3565b84845f5160206110eb5f395f51905f526020604051868152a36109e4565b84845f5160206110cb5f395f51905f5260408051858152866020820152a36109de565b50806109d6565b809192935060041161009c576040908201829003600319011261009c57610a946024610a8d60048401610579565b9201610579565b6001600160a01b03908116918116907f0000000000000000000000005cdc654bc149f72509442fa9c12762b269f34c2316301461015a57806107d7575f5160206110ab5f395f51905f526020476107ce8186610fed565b508260041161009c5780356001600160e01b031916610677565b90601f801991011681019081106001600160401b03821117610b2657604052565b634e487b7160e01b5f52604160045260245ffd5b6001600160a01b039182168152911660208201526040810191909152606081019190915260800190565b909291906001600160a01b0381169081610be75750833193828510610bbe5760407f8eb274f829e211950a0a80ca2046d254c0c8cf86b579688edc70b9c0cb92e9a891815194855286602086015260018060a01b031693a3565b6305176aa160e21b5f9081526001600160a01b0390911660045260248390526044859052606490fd5b6040516370a0823160e01b81526001600160a01b038616600482018190529195919391602082602481875afa918215610cb5575f92610c81575b508196838310610c5f5750507f8eb274f829e211950a0a80ca2046d254c0c8cf86b579688edc70b9c0cb92e9a89160409182519182526020820152a3565b610c7d8385604051948594631041008560e21b865260048601610b3a565b0390fd5b9091506020813d602011610cad575b81610c9d60209383610b05565b8101031261009c5751905f610c21565b3d9150610c90565b6040513d5f823e3d90fd5b909291906001600160a01b0381169081610d44575083319382851015610d1b5760407fc6266c8cbc3af1d4b8fb9826a624b306af6b751bf87154dc0123cc02b86c933491815194855286602086015260018060a01b031693a3565b63a3401cbd60e01b5f9081526001600160a01b0390911660045260248390526044859052606490fd5b6040516370a0823160e01b81526001600160a01b038616600482018190529195919391602082602481875afa918215610cb5575f92610ddb575b50819683831015610dbd5750507fc6266c8cbc3af1d4b8fb9826a624b306af6b751bf87154dc0123cc02b86c93349160409182519182526020820152a3565b610c7d8385604051948594630c059d1960e41b865260048601610b3a565b9091506020813d602011610e07575b81610df760209383610b05565b8101031261009c5751905f610d7e565b3d9150610dea565b6001600160a01b031680610e235750333190565b6020602491604051928380926370a0823160e01b82523360048301525afa908115610cb5575f91610e52575090565b90506020813d602011610e79575b81610e6d60209383610b05565b8101031261009c575190565b3d9150610e60565b6040516370a0823160e01b815230600482015290602090829060249082906001600160a01b03165afa908115610cb5575f91610e52575090565b60405163095ea7b360e01b60208083019182526001600160a01b037f0000000000000000000000005cdc654bc149f72509442fa9c12762b269f34c238181166024860152604480860197909752958452929390921691905f90610f1f606486610b05565b84519082855af15f513d82610f87575b505015610f3b57505050565b60405163095ea7b360e01b60208201526001600160a01b0390931660248401525f6044808501919091528352610f8592610f8090610f7a606482610b05565b82611052565b611052565b565b909150610f9b5750803b15155b5f80610f2f565b600114610f94565b60405163a9059cbb60e01b60208201526001600160a01b03929092166024830152604480830193909352918152610f8591610fdf606483610b05565b6001600160a01b0316611052565b5f918291829182916001600160a01b03165af13d1561104d573d6001600160401b038111610b26576040519061102d601f8201601f191660200183610b05565b81525f60203d92013e5b1561103e57565b633d2cec6f60e21b5f5260045ffd5b611037565b905f602091828151910182855af115610cb5575f513d6110a157506001600160a01b0381163b155b6110815750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b6001141561107a56feed679328aebf74ede77ae09efcf36e90244f83643dadac1c2d9f0b21a46f6ab7bc530e98937a005fa590a15899ce2e21e1bfa93730e6dfe36bcd7a041d6abf85f40cc8c1a1d17359049ba500cfc894596a692cffc9d03943cd92ec2e159cf6aea26469706673582212205cc4495f5ca3155eab85ef2314e2f889ae13e90761f907d299793195fb84665564736f6c634300081e0033
IERC1363.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol) pragma solidity >=0.6.2; 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); }
IERC165.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol) pragma solidity >=0.4.16; import {IERC165} from "../utils/introspection/IERC165.sol";
IERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol) pragma solidity >=0.4.16; import {IERC20} from "../token/ERC20/IERC20.sol";
IERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol) pragma solidity >=0.4.16; /** * @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); }
SafeERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.3.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); } }
IERC165.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol) pragma solidity >=0.4.16; /** * @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); }
IDelegatedExtension.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; /// @title IDelegatedExtension /// @author Agustin Aguilar /// @notice Interface for the delegated extension module interface IDelegatedExtension { /// @notice Handle a sequence delegate call /// @param _opHash The operation hash /// @param _startingGas The starting gas /// @param _index The index /// @param _numCalls The number of calls /// @param _space The space /// @param _data The data function handleSequenceDelegateCall( bytes32 _opHash, uint256 _startingGas, uint256 _index, uint256 _numCalls, uint256 _space, bytes calldata _data ) external; }
TrailsTokenSweeper.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IDelegatedExtension} from "wallet-contracts-v3/modules/interfaces/IDelegatedExtension.sol"; /** * @title TrailsTokenSweeper * @author Shun Kakinoki * @dev This contract can be used to sweep native tokens or ERC20 tokens from this contract to a specified address. */ contract TrailsTokenSweeper is IDelegatedExtension { // ------------------------------------------------------------------------- // Libraries // ------------------------------------------------------------------------- using SafeERC20 for IERC20; // ------------------------------------------------------------------------- // Errors // ------------------------------------------------------------------------- error NativeTransferFailed(); error NotDelegateCall(); error InvalidDelegatedSelector(bytes4 selector); error InsufficientNativeBalance(address account, uint256 required, uint256 available); error InsufficientERC20Balance(address token, address account, uint256 required, uint256 available); error ExcessiveNativeBalance(address account, uint256 maxAllowed, uint256 available); error ExcessiveERC20Balance(address token, address account, uint256 maxAllowed, uint256 available); // ------------------------------------------------------------------------- // Events // ------------------------------------------------------------------------- event Refund(address indexed token, address indexed recipient, uint256 amount); event Sweep(address indexed token, address indexed recipient, uint256 amount); event RefundAndSweep( address indexed token, address indexed refundRecipient, uint256 refundAmount, address indexed sweepRecipient, uint256 actualRefund, uint256 remaining ); event ValidateBalance(address indexed token, address indexed account, uint256 minExpected, uint256 current); event ValidateLesserThanBalance( address indexed token, address indexed account, uint256 maxAllowed, uint256 current ); event ActualRefund(address indexed token, address indexed recipient, uint256 expected, uint256 actual); // ------------------------------------------------------------------------- // Constants / Modifiers // ------------------------------------------------------------------------- address private immutable SELF = address(this); modifier onlyDelegatecall() { if (address(this) == SELF) revert NotDelegateCall(); _; } // ------------------------------------------------------------------------- // View Functions // ------------------------------------------------------------------------- /** * @notice Gets the balance of a given token. * @param _token The address of the token. Use address(0) for the native token. * @return The balance of the token. */ function getBalance(address _token) public view returns (uint256) { if (_token == address(0)) { return msg.sender.balance; } else { return IERC20(_token).balanceOf(msg.sender); } } /** * @notice Ensures `account` has at least `minExpected` balance for `token`. * @dev Use `token == address(0)` to validate native balance. Reverts with * specific errors on failure and returns the current balance on success. * @param token The token address to check. Use address(0) for native. * @param account The account whose balance to validate. * @param minExpected The minimum required balance. * @return current The current balance of `account` for the given asset. */ function validateBalance(address token, address account, uint256 minExpected) public returns (uint256 current) { if (token == address(0)) { current = account.balance; if (current < minExpected) { revert InsufficientNativeBalance(account, minExpected, current); } emit ValidateBalance(token, account, minExpected, current); } else { current = IERC20(token).balanceOf(account); if (current < minExpected) { revert InsufficientERC20Balance(token, account, minExpected, current); } emit ValidateBalance(token, account, minExpected, current); } } /** * @notice Ensures `account` has less than `maxAllowed` balance for `token`. * @dev Use `token == address(0)` to validate native balance. Reverts with * specific errors on failure and returns the current balance on success. * @param token The token address to check. Use address(0) for native. * @param account The account whose balance to validate. * @param maxAllowed The maximum allowed balance (exclusive). * @return current The current balance of `account` for the given asset. */ function validateLesserThanBalance(address token, address account, uint256 maxAllowed) public returns (uint256 current) { if (token == address(0)) { current = account.balance; if (current >= maxAllowed) { revert ExcessiveNativeBalance(account, maxAllowed, current); } emit ValidateLesserThanBalance(token, account, maxAllowed, current); } else { current = IERC20(token).balanceOf(account); if (current >= maxAllowed) { revert ExcessiveERC20Balance(token, account, maxAllowed, current); } emit ValidateLesserThanBalance(token, account, maxAllowed, current); } } // ------------------------------------------------------------------------- // Validate and Execute // ------------------------------------------------------------------------- /** * @notice Validates minimum balance then sweeps the entire balance to recipient. * @dev Use address(0) for native token. Runs under delegatecall context. * @param _token The asset to sweep. address(0) for native. * @param _minExpected The minimum required balance before sweeping. * @param _recipient The address to receive the sweep. */ function validateAndSweep(address _token, uint256 _minExpected, address _recipient) public payable onlyDelegatecall { // Validate required minimum balance first; will revert if insufficient. validateBalance(_token, address(this), _minExpected); // Sweep the balance to the recipient and emit events. sweep(_token, _recipient); } /** * @notice Validates maximum balance then sweeps the entire balance to recipient. * @dev Use address(0) for native token. Runs under delegatecall context. * @param _token The asset to sweep. address(0) for native. * @param _maxAllowed The maximum allowed balance before sweeping (exclusive). * @param _recipient The address to receive the sweep. */ function validateLesserThanAndSweep(address _token, uint256 _maxAllowed, address _recipient) public payable onlyDelegatecall { // Validate that balance is less than maximum allowed; will revert if excessive. validateLesserThanBalance(_token, address(this), _maxAllowed); // Sweep the balance to the recipient and emit events. sweep(_token, _recipient); } // ------------------------------------------------------------------------- // Internal Helpers // ------------------------------------------------------------------------- function _ensureERC20Approval(address _token, uint256 _amount) internal { IERC20 erc20 = IERC20(_token); SafeERC20.forceApprove(erc20, SELF, _amount); } function _transferNative(address _to, uint256 _amount) internal { (bool success,) = payable(_to).call{value: _amount}(""); if (!success) revert NativeTransferFailed(); } function _transferERC20(address _token, address _to, uint256 _amount) internal { IERC20 erc20 = IERC20(_token); SafeERC20.safeTransfer(erc20, _to, _amount); } function _nativeBalance() internal view returns (uint256) { return address(this).balance; } function _erc20Balance(address _token) internal view returns (uint256) { return IERC20(_token).balanceOf(address(this)); } // ------------------------------------------------------------------------- // External Functions // ------------------------------------------------------------------------- /** * @notice Approves the sweeper if ERC20, then sweeps the entire balance to recipient. * @dev Approval is set for `SELF` (the sweeper contract) on the wallet (delegatecall context). * For native tokens, approval is skipped and the native balance is swept. * @param _token The address of the token to sweep. Use address(0) for the native token. * @param _recipient The address to send the swept tokens to. */ function sweep(address _token, address _recipient) public payable onlyDelegatecall { if (_token == address(0)) { uint256 amount = _nativeBalance(); _transferNative(_recipient, amount); emit Sweep(_token, _recipient, amount); } else { uint256 amount = _erc20Balance(_token); _ensureERC20Approval(_token, amount); _transferERC20(_token, _recipient, amount); emit Sweep(_token, _recipient, amount); } } /** * @notice Refunds up to `_refundAmount` to `_refundRecipient`, then sweeps any remaining balance to `_sweepRecipient`. * @dev For ERC20 tokens, sets infinite approval to `SELF` in delegatecall context for compatibility, then transfers. * @param _token The token address to operate on. Use address(0) for native. * @param _refundRecipient Address receiving the refund portion. * @param _refundAmount Maximum amount to refund. * @param _sweepRecipient Address receiving the remaining balance. */ function refundAndSweep(address _token, address _refundRecipient, uint256 _refundAmount, address _sweepRecipient) public payable onlyDelegatecall { if (_token == address(0)) { uint256 current = _nativeBalance(); uint256 actualRefund = _refundAmount > current ? current : _refundAmount; if (actualRefund != _refundAmount) { emit ActualRefund(_token, _refundRecipient, _refundAmount, actualRefund); } if (actualRefund > 0) { _transferNative(_refundRecipient, actualRefund); emit Refund(_token, _refundRecipient, actualRefund); } uint256 remaining = _nativeBalance(); if (remaining > 0) { _transferNative(_sweepRecipient, remaining); emit Sweep(_token, _sweepRecipient, remaining); } emit RefundAndSweep(_token, _refundRecipient, _refundAmount, _sweepRecipient, actualRefund, remaining); } else { uint256 balance = _erc20Balance(_token); _ensureERC20Approval(_token, balance); uint256 actualRefund = _refundAmount > balance ? balance : _refundAmount; if (actualRefund != _refundAmount) { emit ActualRefund(_token, _refundRecipient, _refundAmount, actualRefund); } if (actualRefund > 0) { _transferERC20(_token, _refundRecipient, actualRefund); emit Refund(_token, _refundRecipient, actualRefund); } uint256 remaining = _erc20Balance(_token); if (remaining > 0) { _transferERC20(_token, _sweepRecipient, remaining); emit Sweep(_token, _sweepRecipient, remaining); } emit RefundAndSweep(_token, _refundRecipient, _refundAmount, _sweepRecipient, actualRefund, remaining); } } // ------------------------------------------------------------------------- // Sequence Delegated Extension Entry Point // ------------------------------------------------------------------------- /** * @notice Entry point for Sequence delegatecall routing. * @dev The wallet module delegatecalls this function with the original call data in `_data`. * We decode the selector and dispatch to the corresponding function in this contract. * Execution context is that of the wallet (delegatecall), which is required for sweeping. */ function handleSequenceDelegateCall( bytes32, /* _opHash */ uint256, /* _startingGas */ uint256, /* _index */ uint256, /* _numCalls */ uint256, /* _space */ bytes calldata _data ) external override onlyDelegatecall { bytes4 selector; if (_data.length >= 4) { selector = bytes4(_data[0:4]); } if (selector == this.sweep.selector) { (address token, address recipient) = abi.decode(_data[4:], (address, address)); sweep(token, recipient); return; } if (selector == this.refundAndSweep.selector) { (address token, address refundRecipient, uint256 refundAmount, address sweepRecipient) = abi.decode(_data[4:], (address, address, uint256, address)); refundAndSweep(token, refundRecipient, refundAmount, sweepRecipient); return; } if (selector == this.validateAndSweep.selector) { (address token, uint256 minExpected, address recipient) = abi.decode(_data[4:], (address, uint256, address)); validateAndSweep(token, minExpected, recipient); return; } if (selector == this.validateLesserThanAndSweep.selector) { (address token, uint256 maxAllowed, address recipient) = abi.decode(_data[4:], (address, uint256, address)); validateLesserThanAndSweep(token, maxAllowed, recipient); return; } if (selector == this.validateBalance.selector) { (address token, address account, uint256 minExpected) = abi.decode(_data[4:], (address, address, uint256)); validateBalance(token, account, minExpected); return; } if (selector == this.validateLesserThanBalance.selector) { (address token, address account, uint256 maxAllowed) = abi.decode(_data[4:], (address, address, uint256)); validateLesserThanBalance(token, account, maxAllowed); return; } revert InvalidDelegatedSelector(selector); } }
Gas Token: