Address Contract Verified
Address
0x7d2D47e441915Ff9C2D5A6E4A7AAdD5E02722e29
Balance
0 ETH
Nonce
1
Code Size
4928 bytes
Creator
0x3aEDc1e7...BF4d at tx 0x7987631f...5cb3de
Indexed Transactions
0
Contract Bytecode
4928 bytes
0x6080604052600436106100dc575f3560e01c8063a7f173ab1161007c578063d8d11f7811610057578063d8d11f7814610268578063e75235b814610287578063e86637db146102b9578063f698da25146102e5575f80fd5b8063a7f173ab14610216578063affed0e014610235578063b4faba0914610249575f80fd5b806360657de7116100b757806360657de71461018d578063685cb2f0146101a05780637d832974146101bf578063a0e67e2b146101f5575f80fd5b80632c89372f1461011c5780632f54bf6e1461013d5780633408e47014610171575f80fd5b366101185760405134815233907f3d0ce9bfc3ed7d6862dbb28b2dea94561fe714a1b4d019aa8af39730d1ad7c3d9060200160405180910390a2005b5f80fd5b348015610127575f80fd5b5061013b610136366004610d65565b6102f9565b005b348015610148575f80fd5b5061015c610157366004610d9c565b6103e9565b60405190151581526020015b60405180910390f35b34801561017c575f80fd5b50465b604051908152602001610168565b61015c61019b366004610e0f565b610422565b3480156101ab575f80fd5b5061013b6101ba366004610ebe565b61069b565b3480156101ca575f80fd5b5061017f6101d9366004610f77565b600260209081525f928352604080842090915290825290205481565b348015610200575f80fd5b506102096107ee565b6040516101689190610fa1565b348015610221575f80fd5b5061013b610230366004610fed565b6108fb565b348015610240575f80fd5b5061017f5f5481565b348015610254575f80fd5b5061013b610263366004611018565b61096e565b348015610273575f80fd5b5061017f610282366004610ebe565b61098d565b348015610292575f80fd5b507f000000000000000000000000000000000000000000000000000000000000000361017f565b3480156102c4575f80fd5b506102d86102d3366004610ebe565b6109b9565b60405161016891906110d6565b3480156102f0575f80fd5b5061017f610a90565b60015f8181526020919091527fcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f546001600160a01b03165b6001600160a01b038116600114610397576001600160a01b0381165f90815260026020908152604080832087845290915290205415610378578161037481611136565b9250505b6001600160a01b039081165f9081526001602052604090205416610331565b828210156103e35760405162461bcd60e51b81526020600482015260146024820152734e6f7420656e6f75676820617070726f76616c7360601b60448201526064015b60405180910390fd5b50505050565b5f6001600160a01b03821660011480159061041c57506001600160a01b038281165f908152600160205260409020541615155b92915050565b5f61042c336103e9565b6104785760405162461bcd60e51b815260206004820152601960248201527f4578656375746f72206d75737420626520616e206f776e65720000000000000060448201526064016103da565b5f8061048e8d8d8d8d8d8d8d8d8d8d5f546109b9565b5f80549192508061049e83611136565b90915550508051602082012091506104b5826108fb565b506104e1603f6104c689604061114e565b6104d09190611165565b6104dc896109c4611184565b610ae6565b6104ed906101f4611184565b5a101561052f5760405162461bcd60e51b815260206004820152601060248201526f496e73756666696369656e742067617360801b60448201526064016103da565b5f5a905061058f8d8d8d8d8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152508f9250508b15905061057c578c610afe565b6109c45a61058a9190611197565b610afe565b92505a61059c9082611197565b905082806105a957508715155b806105b357508515155b6105f35760405162461bcd60e51b8152602060048201526011602482015270115c9c9bdc88191d5c9a5b99c818d85b1b607a1b60448201526064016103da565b5f861561060a576106078289898989610b42565b90505b831561064f57827f442e715f626346e8c54381002da614f62bee8d27386535b2521ec8540898556e8260405161064291815260200190565b60405180910390a261068a565b827f23428b18acfb3ea64b08dc0c1d296ea9c09702c09083ca5272e64d115b687d238260405161068191815260200190565b60405180910390a25b5050509a9950505050505050505050565b335f908152600160205260409020546001600160a01b03166106eb5760405162461bcd60e51b81526020600482015260096024820152682737ba1037bbb732b960b91b60448201526064016103da565b5f6107008c8c8c8c8c8c8c8c8c8c5f5461098d565b90508181146107515760405162461bcd60e51b815260206004820152601960248201527f496e636f7272656374206461746120746f20617070726f76650000000000000060448201526064016103da565b600160025f336001600160a01b03166001600160a01b031681526020019081526020015f205f8481526020019081526020015f2081905550336001600160a01b0316827f59434c6eace175e1c3cdb3eea4b1a1b9fe725c224636373ccb02cbf2cb8300568e8e8e8e8e8e8e8e8e8e5f546040516107d89b9a999897969594939291906111de565b60405180910390a3505050505050505050505050565b60605f7f000000000000000000000000000000000000000000000000000000000000000567ffffffffffffffff81111561082a5761082a611004565b604051908082528060200260200182016040528015610853578160200160208202803683370190505b5060015f8181526020919091527fcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f54919250906001600160a01b03165b6001600160a01b0381166001146108f357808383815181106108b4576108b4611278565b6001600160a01b039283166020918202929092018101919091529181165f908152600190925260409091205416816108eb81611136565b925050610890565b509092915050565b7f0000000000000000000000000000000000000000000000000000000000000003806109605760405162461bcd60e51b8152602060048201526014602482015273151a1c995cda1bdb19081a5cc81b9bdd081cd95d60621b60448201526064016103da565b61096a82826102f9565b5050565b5f80825160208401855af4805f52503d6020523d5f60403e60403d015ffd5b5f6109a18c8c8c8c8c8c8c8c8c8c8c6109b9565b8051906020012090509b9a5050505050505050505050565b60605f7fbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d85f1b8d8d8d8d6040516109f192919061128c565b604051908190038120610a17949392918e908e908e908e908e908e908e9060200161129b565b60408051601f1981840301815291905280516020909101209050601960f81b600160f81b610a43610a90565b6040516001600160f81b031993841660208201529290911660218301526022820152604281018290526062016040516020818303038152906040529150509b9a5050505050505050505050565b5f7f47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a794692184660408051602081019390935282015230606082015260800160405160208183030381529060405280519060200120905090565b5f81831015610af55781610af7565b825b9392505050565b5f6001836001811115610b1357610b136111aa565b03610b2a575f808551602087018986f49050610b39565b5f80855160208701888a87f190505b95945050505050565b5f806001600160a01b03831615610b595782610b5b565b325b90506001600160a01b038416610c50573a8510610b78573a610b7a565b845b610b848789611184565b610b8e919061114e565b91505f816001600160a01b0316836040515f6040518083038185875af1925050503d805f8114610bd9576040519150601f19603f3d011682016040523d82523d5f602084013e610bde565b606091505b5050905080610c4a5760405162461bcd60e51b815260206004820152603260248201527f4572726f72207768656e20706179696e672061207472616e73616374696f6e20604482015271696e206e61746976652063757272656e637960701b60648201526084016103da565b50610ccf565b84610c5b8789611184565b610c65919061114e565b9150610c72848284610cd9565b610ccf5760405162461bcd60e51b815260206004820152602860248201527f4572726f72207768656e20706179696e672061207472616e73616374696f6e2060448201526734b7103a37b5b2b760c11b60648201526084016103da565b5095945050505050565b604080516001600160a01b03841660248201526044808201849052825180830390910181526064909101909152602080820180516001600160e01b031663a9059cbb60e01b17815282515f93929184919082896127105a03f13d8015610d495760208114610d51575f9350610d5b565b819350610d5b565b5f51158215171593505b5050509392505050565b5f8060408385031215610d76575f80fd5b50508035926020909101359150565b6001600160a01b0381168114610d99575f80fd5b50565b5f60208284031215610dac575f80fd5b8135610af781610d85565b5f8083601f840112610dc7575f80fd5b50813567ffffffffffffffff811115610dde575f80fd5b602083019150836020828501011115610df5575f80fd5b9250929050565b803560028110610e0a575f80fd5b919050565b5f805f805f805f805f806101208b8d031215610e29575f80fd5b8a35610e3481610d85565b995060208b0135985060408b013567ffffffffffffffff811115610e56575f80fd5b610e628d828e01610db7565b9099509750610e75905060608c01610dfc565b955060808b0135945060a08b0135935060c08b0135925060e08b0135610e9a81610d85565b91506101008b0135610eab81610d85565b809150509295989b9194979a5092959850565b5f805f805f805f805f805f6101408c8e031215610ed9575f80fd5b8b35610ee481610d85565b9a5060208c0135995060408c013567ffffffffffffffff811115610f06575f80fd5b610f128e828f01610db7565b909a509850610f25905060608d01610dfc565b965060808c0135955060a08c0135945060c08c0135935060e08c0135610f4a81610d85565b92506101008c0135610f5b81610d85565b809250506101208c013590509295989b509295989b9093969950565b5f8060408385031215610f88575f80fd5b8235610f9381610d85565b946020939093013593505050565b602080825282518282018190525f9190848201906040850190845b81811015610fe15783516001600160a01b031683529284019291840191600101610fbc565b50909695505050505050565b5f60208284031215610ffd575f80fd5b5035919050565b634e487b7160e01b5f52604160045260245ffd5b5f8060408385031215611029575f80fd5b823561103481610d85565b9150602083013567ffffffffffffffff80821115611050575f80fd5b818501915085601f830112611063575f80fd5b81358181111561107557611075611004565b604051601f8201601f19908116603f0116810190838211818310171561109d5761109d611004565b816040528281528860208487010111156110b5575f80fd5b826020860160208301375f6020848301015280955050505050509250929050565b5f602080835283518060208501525f5b81811015611102578581018301518582016040015282016110e6565b505f604082860101526040601f19601f8301168501019250505092915050565b634e487b7160e01b5f52601160045260245ffd5b5f6001820161114757611147611122565b5060010190565b808202811582820484141761041c5761041c611122565b5f8261117f57634e487b7160e01b5f52601260045260245ffd5b500490565b8082018082111561041c5761041c611122565b8181038181111561041c5761041c611122565b634e487b7160e01b5f52602160045260245ffd5b600281106111da57634e487b7160e01b5f52602160045260245ffd5b9052565b6001600160a01b038c81168252602082018c90526101406040830181905282018a90525f90610160908b8d838601375f848d01830152601f8c01601f191684018201925061122f606085018c6111be565b8960808501528860a08501528760c085015280871660e085015250506112616101008301856001600160a01b03169052565b826101208301529c9b505050505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b818382375f9101908152919050565b8b81526001600160a01b038b81166020830152604082018b9052606082018a90526101608201906112cf608084018b6111be565b60a083019890985260c082019690965260e081019490945291851661010084015290931661012082015261014001919091529594505050505056fea264697066735822122058826768861d7c24c7a0c1db8a43856e485aa53b96a17ff3dd0d0c2c62b0ce8364736f6c63430008160033
Verified Source Code Full Match
Compiler: v0.8.22+commit.4fc1097e
EVM: shanghai
Optimization: Yes (200 runs)
Multisig.sol 490 lines
// SPDX-License-Identifier: GPL-3.0
pragma solidity =0.8.22;
contract Multisig {
enum Operation {
Call,
DelegateCall
}
// keccak256(
// "EIP712Domain(uint256 chainId,address verifyingContract)"
// );
bytes32 private constant DOMAIN_SEPARATOR_TYPEHASH = 0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218;
// keccak256(
// "SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)"
// );
bytes32 private constant SAFE_TX_TYPEHASH = 0xbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d8;
event ApproveHash(
bytes32 indexed approvedHash,
address indexed owner,
address to,
uint256 value,
bytes data,
Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver,
uint256 nonce
);
event ExecutionFailure(bytes32 indexed txHash, uint256 payment);
event ExecutionSuccess(bytes32 indexed txHash, uint256 payment);
event SafeReceived(address indexed sender, uint256 value);
uint256 public nonce;
mapping(address => address) internal owners;
uint256 internal immutable ownerCount;
uint256 internal immutable threshold;
address internal constant SENTINEL_OWNERS = address(0x1);
// mapping to keep track of all hashes (message or transaction) that have been approved by ANY owners
mapping(address => mapping(bytes32 => uint256)) public approvedHashes;
constructor(address[] memory _owners, uint256 _threshold) {
// validate that threshold is smaller than number of added owners
require(_threshold <= _owners.length, "Too big threshold");
// there has to be at least one Safe owner
require(_threshold >= 1, "Threshold can't be equal to zero");
// initializing Safe owners
address currentOwner = SENTINEL_OWNERS;
for (uint256 i = 0; i < _owners.length; i++) {
// owner address cannot be null
address owner = _owners[i];
require(owner != address(0) && owner != SENTINEL_OWNERS && owner != address(this) && currentOwner != owner, "Incorrect owner address");
// no duplicate owners allowed
require(owners[owner] == address(0), "Owners' addresses must not be repeated");
owners[currentOwner] = owner;
currentOwner = owner;
}
owners[currentOwner] = SENTINEL_OWNERS;
ownerCount = _owners.length;
threshold = _threshold;
}
///////////////////////////////
/// VIEW FUNCTIONS
///////////////////////////////
/**
* @notice returns the ID of the chain the contract is currently deployed on
* @return the ID of the current chain as a uint256
*/
function getChainId() public view returns (uint256) {
uint256 id;
// solhint-disable-next-line no-inline-assembly
assembly {
id := chainid()
}
return id;
}
/**
* @dev returns the domain separator for this contract, as defined in the EIP-712 standard
* @return bytes32 the domain separator hash
*/
function domainSeparator() public view returns (bytes32) {
return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, getChainId(), this));
}
/**
* @notice returns the pre-image of the transaction hash (see getTransactionHash)
* @param to destination address
* @param value ether value
* @param data data payload
* @param operation operation type
* @param safeTxGas gas that should be used for the safe transaction
* @param baseGas gas costs for that are independent of the transaction execution(e.g. base transaction fee, signature check, payment of the refund)
* @param gasPrice maximum gas price that should be used for this transaction
* @param gasToken token address (or 0 if ETH) that is used for the payment
* @param refundReceiver address of receiver of gas payment (or 0 if tx.origin)
* @param _nonce transaction nonce
* @return transaction hash bytes
*/
function encodeTransactionData(
address to,
uint256 value,
bytes calldata data,
Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address refundReceiver,
uint256 _nonce
) public view returns (bytes memory) {
bytes32 safeTxHash = keccak256(
abi.encode(
SAFE_TX_TYPEHASH,
to,
value,
keccak256(data),
operation,
safeTxGas,
baseGas,
gasPrice,
gasToken,
refundReceiver,
_nonce
)
);
return abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator(), safeTxHash);
}
/**
* @notice checks whether the signature provided is valid for the provided data and hash. Reverts otherwise
* @param dataHash hash of the data (could be either a message hash or transaction hash)
*/
function checkApprovals(bytes32 dataHash) public view {
// load threshold to avoid multiple storage loads
uint256 _threshold = threshold;
// check that a threshold is set
require(_threshold > 0, "Threshold is not set");
checkNApprovals(dataHash, _threshold);
}
/**
* @notice checks whether the signature provided is valid for the provided data and hash. Reverts otherwise
* @dev since the EIP-1271 does an external call, be mindful of reentrancy attacks
* @param dataHash hash of the data (could be either a message hash or transaction hash)
* @param requiredSignatures amount of required valid signatures
*/
function checkNApprovals(bytes32 dataHash, uint256 requiredSignatures) public view {
uint256 count = 0;
address currentOwner = owners[SENTINEL_OWNERS];
while (currentOwner != SENTINEL_OWNERS) {
if (approvedHashes[currentOwner][dataHash] != 0) {
count++;
}
currentOwner = owners[currentOwner];
}
require(count >= requiredSignatures, "Not enough approvals");
}
/**
* @notice returns transaction hash to be signed by owners
* @param to destination address
* @param value ether value
* @param data data payload
* @param operation operation type
* @param safeTxGas fas that should be used for the safe transaction
* @param baseGas gas costs for data used to trigger the safe transaction
* @param gasPrice maximum gas price that should be used for this transaction
* @param gasToken token address (or 0 if ETH) that is used for the payment
* @param refundReceiver address of receiver of gas payment (or 0 if tx.origin)
* @param _nonce transaction nonce
* @return transaction hash
*/
function getTransactionHash(
address to,
uint256 value,
bytes calldata data,
Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address refundReceiver,
uint256 _nonce
) public view returns (bytes32) {
return keccak256(encodeTransactionData(to, value, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, _nonce));
}
/**
* @notice returns the number of required confirmations for a Safe transaction aka the threshold
* @return threshold number
*/
function getThreshold() public view returns (uint256) {
return threshold;
}
/**
* @notice returns if `owner` is an owner of the Safe
* @return boolean if owner is an owner of the Safe
*/
function isOwner(address owner) public view returns (bool) {
return owner != SENTINEL_OWNERS && owners[owner] != address(0);
}
/**
* @notice returns a list of Safe owners
* @return array of Safe owners
*/
function getOwners() public view returns (address[] memory) {
address[] memory array = new address[](ownerCount);
// populate return array
uint256 index = 0;
address currentOwner = owners[SENTINEL_OWNERS];
while (currentOwner != SENTINEL_OWNERS) {
array[index] = currentOwner;
currentOwner = owners[currentOwner];
index++;
}
return array;
}
///////////////////////////////
/// INTERNAL FUNCTIONS
///////////////////////////////
/**
* @notice handles the payment for a Safe transaction
* @param gasUsed gas used by the Safe transaction
* @param baseGas gas costs that are independent of the transaction execution (e.g. base transaction fee, signature check, payment of the refund)
* @param gasPrice gas price that should be used for the payment calculation
* @param gasToken token address (or 0 if ETH) that is used for the payment
* @return payment The amount of payment made in the specified token
*/
function handlePayment(
uint256 gasUsed,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver
) private returns (uint256 payment) {
// solhint-disable-next-line avoid-tx-origin
address payable receiver = refundReceiver == address(0) ? payable(tx.origin) : refundReceiver;
if (gasToken == address(0)) {
// for ETH we will only adjust the gas price to not be higher than the actual used gas price
payment = (gasUsed + baseGas) * (gasPrice < tx.gasprice ? gasPrice : tx.gasprice);
(bool sent, ) = receiver.call{value: payment}("");
require(sent, "Error when paying a transaction in native currency");
} else {
payment = (gasUsed + baseGas) * (gasPrice);
require(transferToken(gasToken, receiver, payment), "Error when paying a transaction in token");
}
}
/**
* @notice transfers a token and returns a boolean if it was a success
* @dev it checks the return data of the transfer call and returns true if the transfer was successful
* it doesn't check if the `token` address is a contract or not
* @param token token that should be transferred
* @param receiver receiver to whom the token should be transferred
* @param amount the amount of tokens that should be transferred
* @return transferred Returns true if the transfer was successful
*/
function transferToken(address token, address receiver, uint256 amount) internal returns (bool transferred) {
// 0xa9059cbb - keccak256("transfer(address,uint256)")
bytes memory data = abi.encodeWithSelector(0xa9059cbb, receiver, amount);
// solhint-disable-next-line no-inline-assembly
assembly {
// We write the return value to scratch space.
// See https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html#layout-in-memory
let success := call(sub(gas(), 10000), token, 0, add(data, 0x20), mload(data), 0, 0x20)
switch returndatasize()
case 0 {
transferred := success
}
case 0x20 {
transferred := iszero(or(iszero(success), iszero(mload(0))))
}
default {
transferred := 0
}
}
}
/**
* @notice executes either a delegatecall or a call with provided parameters
* @dev this method doesn't perform any sanity check of the transaction, such as:
* - if the contract at `to` address has code or not
* it is the responsibility of the caller to perform such checks
* @param to destination address
* @param value ether value
* @param data data payload
* @param operation operation type
* @return success boolean flag indicating if the call succeeded
*/
function execute(
address to,
uint256 value,
bytes memory data,
Operation operation,
uint256 txGas
) internal returns (bool success) {
if (operation == Operation.DelegateCall) {
// solhint-disable-next-line no-inline-assembly
assembly {
success := delegatecall(txGas, to, add(data, 0x20), mload(data), 0, 0)
}
} else {
// solhint-disable-next-line no-inline-assembly
assembly {
success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0)
}
}
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
///////////////////////////////
/// PUBLIC FUNCTIONS
///////////////////////////////
/** @notice executes a `operation` {0: Call, 1: DelegateCall}} transaction to `to` with `value` (Native Currency)
* and pays `gasPrice` * `gasLimit` in `gasToken` token to `refundReceiver`
* @dev the fees are always transferred, even if the user transaction fails
* this method doesn't perform any sanity check of the transaction, such as:
* - if the contract at `to` address has code or not
* - if the `gasToken` is a contract or not
* it is the responsibility of the caller to perform such checks
* @param to destination address of Safe transaction
* @param value ether value of Safe transaction
* @param data data payload of Safe transaction
* @param operation operation type of Safe transaction
* @param safeTxGas gas that should be used for the Safe transaction
* @param baseGas gas costs that are independent of the transaction execution(e.g. base transaction fee, signature check, payment of the refund)
* @param gasPrice gas price that should be used for the payment calculation
* @param gasToken token address (or 0 if ETH) that is used for the payment
* @param refundReceiver address of receiver of gas payment (or 0 if tx.origin)
* @return success Boolean indicating transaction's success
*/
function execTransaction(
address to,
uint256 value,
bytes calldata data,
Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver
) public payable virtual returns (bool success) {
require(isOwner(msg.sender), "Executor must be an owner");
bytes32 txHash;
// use scope here to limit variable lifetime and prevent `stack too deep` errors
{
bytes memory txHashData = encodeTransactionData(
// transaction info
to,
value,
data,
operation,
safeTxGas,
// payment info
baseGas,
gasPrice,
gasToken,
refundReceiver,
// signature info
nonce
);
// increase nonce and execute transaction.
nonce++;
txHash = keccak256(txHashData);
checkApprovals(txHash);
}
// we require some gas to emit the events (at least 2500) after the execution and some to perform code until the execution (500)
// we also include the 1/64 in the check that is not send along with a call to counteract potential shortings because of EIP-150
require(gasleft() >= max((safeTxGas * 64) / 63, safeTxGas + 2500) + 500, "Insufficient gas");
// use scope here to limit variable lifetime and prevent `stack too deep` errors
{
uint256 gasUsed = gasleft();
// if the gasPrice is 0 we assume that nearly all available gas can be used (it is always more than safeTxGas)
// we only substract 2500 (compared to the 3000 before) to ensure that the amount passed is still higher than safeTxGas
success = execute(to, value, data, operation, gasPrice == 0 ? (gasleft() - 2500) : safeTxGas);
gasUsed = gasUsed - gasleft();
// if no safeTxGas and no gasPrice was set (e.g. both are 0), then the internal tx is required to be successful
// this makes it possible to use `estimateGas` without issues, as it searches for the minimum gas where the tx doesn't revert
require(success || safeTxGas != 0 || gasPrice != 0, "Error during call");
// we transfer the calculated tx costs to the tx.origin to avoid sending it to intermediate contracts that have made calls
uint256 payment = 0;
if (gasPrice > 0) {
payment = handlePayment(gasUsed, baseGas, gasPrice, gasToken, refundReceiver);
}
if (success) emit ExecutionSuccess(txHash, payment);
else emit ExecutionFailure(txHash, payment);
}
}
/**
* @notice marks hash `hashToApprove` as approved
* @dev this can be used with a pre-approved hash transaction signature
* IMPORTANT: the approved hash stays approved forever. There's no revocation mechanism, so it behaves similarly to ECDSA signatures
* @param hashToApprove the hash to mark as approved for signatures that are verified by this contract
*/
function approveHash(
address to,
uint256 value,
bytes calldata data,
Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver,
bytes32 hashToApprove
) external {
require(owners[msg.sender] != address(0), "Not owner");
bytes32 txHash = getTransactionHash(
to,
value,
data,
operation,
safeTxGas,
baseGas,
gasPrice,
gasToken,
refundReceiver,
nonce
);
require(txHash == hashToApprove, "Incorrect data to approve");
approvedHashes[msg.sender][hashToApprove] = 1;
emit ApproveHash(
hashToApprove,
msg.sender,
to,
value,
data,
operation,
safeTxGas,
baseGas,
gasPrice,
gasToken,
refundReceiver,
nonce
);
}
/**
* @dev performs a delegatecall on a targetContract in the context of self
* internally reverts execution to avoid side effects (making it static)
*
* this method reverts with data equal to `abi.encode(bool(success), bytes(response))`
* specifically, the `returndata` after a call to this method will be:
* `success:bool || response.length:uint256 || response:bytes`
*
* @param targetContract address of the contract containing the code to execute
* @param calldataPayload calldata that should be sent to the target contract (encoded method name and arguments)
*/
function simulateAndRevert(address targetContract, bytes memory calldataPayload) external {
// solhint-disable-next-line no-inline-assembly
assembly {
let success := delegatecall(gas(), targetContract, add(calldataPayload, 0x20), mload(calldataPayload), 0, 0)
mstore(0x00, success)
mstore(0x20, returndatasize())
returndatacopy(0x40, 0, returndatasize())
revert(0, add(returndatasize(), 0x40))
}
}
/**
* @notice Receive function accepts native currency transactions.
* @dev Emits an event with sender and received value.
*/
receive() external payable {
emit SafeReceived(msg.sender, msg.value);
}
}
Read Contract
approvedHashes 0x7d832974 → uint256
checkApprovals 0xa7f173ab
checkNApprovals 0x2c89372f
domainSeparator 0xf698da25 → bytes32
encodeTransactionData 0xe86637db → bytes
getChainId 0x3408e470 → uint256
getOwners 0xa0e67e2b → address[]
getThreshold 0xe75235b8 → uint256
getTransactionHash 0xd8d11f78 → bytes32
isOwner 0x2f54bf6e → bool
nonce 0xaffed0e0 → uint256
Write Contract 3 functions
These functions modify contract state and require a wallet transaction to execute.
approveHash 0x685cb2f0
address to
uint256 value
bytes data
uint8 operation
uint256 safeTxGas
uint256 baseGas
uint256 gasPrice
address gasToken
address refundReceiver
bytes32 hashToApprove
execTransaction 0x60657de7
address to
uint256 value
bytes data
uint8 operation
uint256 safeTxGas
uint256 baseGas
uint256 gasPrice
address gasToken
address refundReceiver
returns: bool
simulateAndRevert 0xb4faba09
address targetContract
bytes calldataPayload
Recent Transactions
No transactions found for this address