More Info
Private Name Tags
ContractCreator
Latest 1 internal transaction
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
60221252 | 4 mins ago | Contract Creation | 0 MATIC |
Loading...
Loading
Minimal Proxy Contract for 0x6b423d3885b4877b5760e149364f85f185f477ad
Contract Name:
ChannelImplementation
Compiler Version
v0.8.9+commit.e5eed63a
Contract Source Code (Solidity)
/** *Submitted for verification at polygonscan.com on 2022-05-03 */ // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.9; library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } else if (error == RecoverError.InvalidSignatureV) { revert("ECDSA: invalid signature 'v' value"); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature` or error string. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { // Check the signature length // - case 65: r,s,v signature (standard) // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else if (signature.length == 64) { bytes32 r; bytes32 vs; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { r := mload(add(signature, 0x20)) vs := mload(add(signature, 0x40)) } return tryRecover(hash, r, vs); } else { return (address(0), RecoverError.InvalidSignatureLength); } } function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, signature); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.3._ */ function tryRecover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address, RecoverError) { bytes32 s; uint8 v; assembly { s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) v := add(shr(255, vs), 27) } return tryRecover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. * * _Available since v4.2._ */ function recover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. * * _Available since v4.3._ */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS); } if (v != 27 && v != 28) { return (address(0), RecoverError.InvalidSignatureV); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature); } return (signer, RecoverError.NoError); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } } interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /** * @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); } abstract contract IERC20Token is IERC20 { function upgrade(uint256 value) public virtual; } interface IHermesContract { enum Status { Active, Paused, Punishment, Closed } function initialize(address _token, address _operator, uint16 _hermesFee, uint256 _minStake, uint256 _maxStake, address payable _routerAddress) external; function openChannel(address _party, uint256 _amountToLend) external; function getOperator() external view returns (address); function getStake() external view returns (uint256); function getStatus() external view returns (Status); } interface IUniswapV2Router { function factory() external pure returns (address); function WETH() external pure returns (address); function addLiquidity( address tokenA, address tokenB, uint amountADesired, uint amountBDesired, uint amountAMin, uint amountBMin, address to, uint deadline ) external returns (uint amountA, uint amountB, uint liquidity); function addLiquidityETH( address token, uint amountTokenDesired, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external payable returns (uint amountToken, uint amountETH, uint liquidity); function removeLiquidity( address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline ) external returns (uint amountA, uint amountB); function removeLiquidityETH( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external returns (uint amountToken, uint amountETH); function removeLiquidityWithPermit( address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountA, uint amountB); function removeLiquidityETHWithPermit( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountToken, uint amountETH); function swapExactTokensForTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); function swapTokensForExactTokens( uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts); function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts); function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts); function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts); function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB); function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut); function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn); function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts); } contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); function owner() public view returns (address) { return _owner; } modifier onlyOwner() { require(_owner == msg.sender || _owner == address(0x0), "Ownable: caller is not the owner"); _; } function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } } contract FundsRecovery is Ownable, ReentrancyGuard { address payable internal fundsDestination; IERC20Token public token; event DestinationChanged(address indexed previousDestination, address indexed newDestination); /** * Setting new destination of funds recovery. */ function setFundsDestination(address payable _newDestination) public virtual onlyOwner { require(_newDestination != address(0)); emit DestinationChanged(fundsDestination, _newDestination); fundsDestination = _newDestination; } /** * Getting funds destination address. */ function getFundsDestination() public view returns (address) { return fundsDestination; } /** * Possibility to recover funds in case they were sent to this address before smart contract deployment */ function claimNativeCoin() public nonReentrant { require(fundsDestination != address(0)); fundsDestination.transfer(address(this).balance); } /** Transfers selected tokens into owner address. */ function claimTokens(address _token) public nonReentrant { require(fundsDestination != address(0)); require(_token != address(token), "native token funds can't be recovered"); uint256 _amount = IERC20Token(_token).balanceOf(address(this)); IERC20Token(_token).transfer(fundsDestination, _amount); } } contract Utils { function getChainID() internal view returns (uint256) { uint256 chainID; assembly { chainID := chainid() } return chainID; } function max(uint a, uint b) internal pure returns (uint) { return a > b ? a : b; } function min(uint a, uint b) internal pure returns (uint) { return a < b ? a : b; } function round(uint a, uint m) internal pure returns (uint ) { return ((a + m - 1) / m) * m; } } contract ChannelImplementation is FundsRecovery, Utils { using ECDSA for bytes32; string constant EXIT_PREFIX = "Exit request:"; uint256 internal lastNonce; struct ExitRequest { uint256 timelock; // block number after which exit can be finalized address beneficiary; // address where funds will be send after finalizing exit request } struct Hermes { address operator; // signing address address contractAddress; // hermes smart contract address, funds will be send there uint256 settled; // total amount already settled by hermes } ExitRequest public exitRequest; Hermes public hermes; address public operator; // channel operator = sha3(IdentityPublicKey)[:20] IUniswapV2Router internal dex; // any uniswap v2 compatible dex router address event PromiseSettled(address beneficiary, uint256 amount, uint256 totalSettled, bytes32 lock); event ExitRequested(uint256 timelock); event Withdraw(address beneficiary, uint256 amount); /* ------------------------------------------- SETUP ------------------------------------------- */ // Fallback function - exchange received ETH into MYST receive() external payable { address[] memory path = new address[](2); path[0] = dex.WETH(); path[1] = address(token); dex.swapExactETHForTokens{value: msg.value}(0, path, address(this), block.timestamp); } // Because of proxy pattern this function is used insted of constructor. // Have to be called right after proxy deployment. function initialize(address _token, address _dexAddress, address _identity, address _hermesId, uint256 _fee) public { require(!isInitialized(), "Is already initialized"); require(_identity != address(0), "Identity can't be zero"); require(_hermesId != address(0), "HermesID can't be zero"); require(_token != address(0), "Token can't be deployd into zero address"); token = IERC20Token(_token); dex = IUniswapV2Router(_dexAddress); // Transfer required fee to msg.sender (most probably Registry) if (_fee > 0) { token.transfer(msg.sender, _fee); } operator = _identity; transferOwnership(operator); hermes = Hermes(IHermesContract(_hermesId).getOperator(), _hermesId, 0); } function isInitialized() public view returns (bool) { return operator != address(0); } /* -------------------------------------- MAIN FUNCTIONALITY ----------------------------------- */ // Settle promise // signedMessage: channelId, totalSettleAmount, fee, hashlock // _lock is random number generated by receiver used in HTLC function settlePromise(uint256 _amount, uint256 _transactorFee, bytes32 _lock, bytes memory _signature) public { bytes32 _hashlock = keccak256(abi.encode(_lock)); address _channelId = address(this); address _signer = keccak256(abi.encodePacked(getChainID(), uint256(uint160(_channelId)), _amount, _transactorFee, _hashlock)).recover(_signature); require(_signer == operator, "have to be signed by channel operator"); // Calculate amount of tokens to be claimed. uint256 _unpaidAmount = _amount - hermes.settled; require(_unpaidAmount > 0, "amount to settle should be greater that already settled"); // If signer has less tokens than asked to transfer, we can transfer as much as he has already // and rest tokens can be transferred via same promise but in another tx // when signer will top up channel balance. uint256 _currentBalance = token.balanceOf(_channelId); if (_unpaidAmount > _currentBalance) { _unpaidAmount = _currentBalance; } // Increase already paid amount hermes.settled = hermes.settled + _unpaidAmount; // Send tokens token.transfer(hermes.contractAddress, _unpaidAmount - _transactorFee); // Pay fee to transaction maker if (_transactorFee > 0) { token.transfer(msg.sender, _transactorFee); } emit PromiseSettled(hermes.contractAddress, _unpaidAmount, hermes.settled, _lock); } // Fast funds withdrawal is possible when hermes agrees that given amount of funds can be withdrawn function fastExit(uint256 _amount, uint256 _transactorFee, address _beneficiary, uint256 _validUntil, bytes memory _operatorSignature, bytes memory _hermesSignature) public { require(_validUntil >= block.timestamp, "Channel: _validUntil have to be greater than or equal to current block timestamp"); address _channelId = address(this); bytes32 _msgHash = keccak256(abi.encodePacked(EXIT_PREFIX, getChainID(), uint256(uint160(_channelId)), _amount, _transactorFee, uint256(uint160(_beneficiary)), _validUntil, lastNonce++)); address _firstSigner = _msgHash.recover(_operatorSignature); require(_firstSigner == operator, "Channel: have to be signed by operator"); address _secondSigner = _msgHash.recover(_hermesSignature); require(_secondSigner == hermes.operator, "Channel: have to be signed by hermes"); // Pay fee to transaction maker if (_transactorFee > 0) { require(_amount >= _transactorFee, "Channel: transactor fee can't be bigger that withdrawal amount"); token.transfer(msg.sender, _transactorFee); } // Withdraw agreed amount uint256 _amountToSend = _amount - _transactorFee; token.transfer(_beneficiary, _amountToSend); emit Withdraw(_beneficiary, _amountToSend); } /* ------------------------------------------ HELPERS ------------------------------------------ */ // Setting new destination of funds recovery. string constant FUNDS_DESTINATION_PREFIX = "Set funds destination:"; function setFundsDestinationByCheque(address payable _newDestination, bytes memory _signature) public { require(_newDestination != address(0)); address _channelId = address(this); address _signer = keccak256(abi.encodePacked(FUNDS_DESTINATION_PREFIX, _channelId, _newDestination, lastNonce++)).recover(_signature); require(_signer == operator, "Channel: have to be signed by proper identity"); emit DestinationChanged(fundsDestination, _newDestination); fundsDestination = _newDestination; } }
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousDestination","type":"address"},{"indexed":true,"internalType":"address","name":"newDestination","type":"address"}],"name":"DestinationChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"timelock","type":"uint256"}],"name":"ExitRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalSettled","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"lock","type":"bytes32"}],"name":"PromiseSettled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"claimNativeCoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"claimTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"exitRequest","outputs":[{"internalType":"uint256","name":"timelock","type":"uint256"},{"internalType":"address","name":"beneficiary","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_transactorFee","type":"uint256"},{"internalType":"address","name":"_beneficiary","type":"address"},{"internalType":"uint256","name":"_validUntil","type":"uint256"},{"internalType":"bytes","name":"_operatorSignature","type":"bytes"},{"internalType":"bytes","name":"_hermesSignature","type":"bytes"}],"name":"fastExit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getFundsDestination","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hermes","outputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"settled","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_dexAddress","type":"address"},{"internalType":"address","name":"_identity","type":"address"},{"internalType":"address","name":"_hermesId","type":"address"},{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"operator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"_newDestination","type":"address"}],"name":"setFundsDestination","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"_newDestination","type":"address"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"setFundsDestinationByCheque","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_transactorFee","type":"uint256"},{"internalType":"bytes32","name":"_lock","type":"bytes32"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"settlePromise","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20Token","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
MATIC | 100.00% | $0.142183 | 20.9146 | $2.97 |
[ 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.