Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 1 from a total of 1 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Withdraw | 12263620 | 1776 days ago | IN | 0 ETH | 0.01075206 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| - | 12263616 | 1776 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Minimal Proxy Contract for 0xd105b6b42206dfa6db00e6a4823bc88efac00476
Contract Name:
ChannelMastercopy
Compiler Version
v0.7.1+commit.f4a555be
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
import "./interfaces/IVectorChannel.sol";
import "./CMCCore.sol";
import "./CMCAsset.sol";
import "./CMCDeposit.sol";
import "./CMCWithdraw.sol";
import "./CMCAdjudicator.sol";
/// @title ChannelMastercopy
/// @author Connext <support@connext.network>
/// @notice Contains the logic used by all Vector multisigs. A proxy to this
/// contract is deployed per-channel using the ChannelFactory.sol.
/// Supports channel adjudication logic, deposit logic, and arbitrary
/// calls when a commitment is double-signed.
contract ChannelMastercopy is
CMCCore,
CMCAsset,
CMCDeposit,
CMCWithdraw,
CMCAdjudicator,
IVectorChannel
{
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
import "./ICMCCore.sol";
import "./ICMCAsset.sol";
import "./ICMCDeposit.sol";
import "./ICMCWithdraw.sol";
import "./ICMCAdjudicator.sol";
interface IVectorChannel is
ICMCCore,
ICMCAsset,
ICMCDeposit,
ICMCWithdraw,
ICMCAdjudicator
{}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
import "./LibERC20.sol";
import "./LibUtils.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/// @title LibAsset
/// @author Connext <support@connext.network>
/// @notice This library contains helpers for dealing with onchain transfers
/// of in-channel assets. It is designed to safely handle all asset
/// transfers out of channel in the event of an onchain dispute. Also
/// safely handles ERC20 transfers that may be non-compliant
library LibAsset {
address constant ETHER_ASSETID = address(0);
function isEther(address assetId) internal pure returns (bool) {
return assetId == ETHER_ASSETID;
}
function getOwnBalance(address assetId) internal view returns (uint256) {
return
isEther(assetId)
? address(this).balance
: IERC20(assetId).balanceOf(address(this));
}
function transferEther(address payable recipient, uint256 amount)
internal
returns (bool)
{
(bool success, bytes memory returnData) =
recipient.call{value: amount}("");
LibUtils.revertIfCallFailed(success, returnData);
return true;
}
function transferERC20(
address assetId,
address recipient,
uint256 amount
) internal returns (bool) {
return LibERC20.transfer(assetId, recipient, amount);
}
// This function is a wrapper for transfers of Ether or ERC20 tokens,
// both standard-compliant ones as well as tokens that exhibit the
// missing-return-value bug.
// Although it behaves very much like Solidity's `transfer` function
// or the ERC20 `transfer` and is, in fact, designed to replace direct
// usage of those, it is deliberately named `unregisteredTransfer`,
// because we need to register every transfer out of the channel.
// Therefore, it should normally not be used directly, with the single
// exception of the `transferAsset` function in `CMCAsset.sol`,
// which combines the "naked" unregistered transfer given below
// with a registration.
// USING THIS FUNCTION SOMEWHERE ELSE IS PROBABLY WRONG!
function unregisteredTransfer(
address assetId,
address payable recipient,
uint256 amount
) internal returns (bool) {
return
isEther(assetId)
? transferEther(recipient, amount)
: transferERC20(assetId, recipient, amount);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
import "./LibUtils.sol";
import "@openzeppelin/contracts/utils/Address.sol";
/// @title LibERC20
/// @author Connext <support@connext.network>
/// @notice This library provides several functions to safely handle
/// noncompliant tokens (i.e. does not return a boolean from
/// the transfer function)
library LibERC20 {
function wrapCall(address assetId, bytes memory callData)
internal
returns (bool)
{
require(Address.isContract(assetId), "LibERC20: NO_CODE");
(bool success, bytes memory returnData) = assetId.call(callData);
LibUtils.revertIfCallFailed(success, returnData);
return returnData.length == 0 || abi.decode(returnData, (bool));
}
function approve(
address assetId,
address spender,
uint256 amount
) internal returns (bool) {
return
wrapCall(
assetId,
abi.encodeWithSignature(
"approve(address,uint256)",
spender,
amount
)
);
}
function transferFrom(
address assetId,
address sender,
address recipient,
uint256 amount
) internal returns (bool) {
return
wrapCall(
assetId,
abi.encodeWithSignature(
"transferFrom(address,address,uint256)",
sender,
recipient,
amount
)
);
}
function transfer(
address assetId,
address recipient,
uint256 amount
) internal returns (bool) {
return
wrapCall(
assetId,
abi.encodeWithSignature(
"transfer(address,uint256)",
recipient,
amount
)
);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
interface ICMCCore {
function setup(address _alice, address _bob) external;
function getAlice() external view returns (address);
function getBob() external view returns (address);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
interface ICMCAsset {
function getTotalTransferred(address assetId)
external
view
returns (uint256);
function getExitableAmount(address assetId, address owner)
external
view
returns (uint256);
function exit(
address assetId,
address owner,
address payable recipient
) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
interface ICMCDeposit {
event AliceDeposited(address assetId, uint256 amount);
function getTotalDepositsAlice(address assetId)
external
view
returns (uint256);
function getTotalDepositsBob(address assetId)
external
view
returns (uint256);
function depositAlice(address assetId, uint256 amount) external payable;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
struct WithdrawData {
address channelAddress;
address assetId;
address payable recipient;
uint256 amount;
uint256 nonce;
address callTo;
bytes callData;
}
interface ICMCWithdraw {
function getWithdrawalTransactionRecord(WithdrawData calldata wd)
external
view
returns (bool);
function withdraw(
WithdrawData calldata wd,
bytes calldata aliceSignature,
bytes calldata bobSignature
) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
import "./Types.sol";
interface ICMCAdjudicator {
struct CoreChannelState {
address channelAddress;
address alice;
address bob;
address[] assetIds;
Balance[] balances;
uint256[] processedDepositsA;
uint256[] processedDepositsB;
uint256[] defundNonces;
uint256 timeout;
uint256 nonce;
bytes32 merkleRoot;
}
struct CoreTransferState {
address channelAddress;
bytes32 transferId;
address transferDefinition;
address initiator;
address responder;
address assetId;
Balance balance;
uint256 transferTimeout;
bytes32 initialStateHash;
}
struct ChannelDispute {
bytes32 channelStateHash;
uint256 nonce;
bytes32 merkleRoot;
uint256 consensusExpiry;
uint256 defundExpiry;
}
struct TransferDispute {
bytes32 transferStateHash;
uint256 transferDisputeExpiry;
bool isDefunded;
}
event ChannelDisputed(
address disputer,
CoreChannelState state,
ChannelDispute dispute
);
event ChannelDefunded(
address defunder,
CoreChannelState state,
ChannelDispute dispute,
address[] assetIds
);
event TransferDisputed(
address disputer,
CoreTransferState state,
TransferDispute dispute
);
event TransferDefunded(
address defunder,
CoreTransferState state,
TransferDispute dispute,
bytes encodedInitialState,
bytes encodedResolver,
Balance balance
);
function getChannelDispute() external view returns (ChannelDispute memory);
function getDefundNonce(address assetId) external view returns (uint256);
function getTransferDispute(bytes32 transferId)
external
view
returns (TransferDispute memory);
function disputeChannel(
CoreChannelState calldata ccs,
bytes calldata aliceSignature,
bytes calldata bobSignature
) external;
function defundChannel(
CoreChannelState calldata ccs,
address[] calldata assetIds,
uint256[] calldata indices
) external;
function disputeTransfer(
CoreTransferState calldata cts,
bytes32[] calldata merkleProofData
) external;
function defundTransfer(
CoreTransferState calldata cts,
bytes calldata encodedInitialTransferState,
bytes calldata encodedTransferResolver,
bytes calldata responderSignature
) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
struct Balance {
uint256[2] amount; // [alice, bob] in channel, [initiator, responder] in transfer
address payable[2] to; // [alice, bob] in channel, [initiator, responder] in transfer
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
/// @title LibUtils
/// @author Connext <support@connext.network>
/// @notice Contains a helper to revert if a call was not successfully
/// made
library LibUtils {
// If success is false, reverts and passes on the revert string.
function revertIfCallFailed(bool success, bytes memory returnData)
internal
pure
{
if (!success) {
assembly {
revert(add(returnData, 0x20), mload(returnData))
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
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);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line no-inline-assembly
assembly { codehash := extcodehash(account) }
return (codehash != accountHash && codehash != 0x0);
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain`call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return _functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
return _functionCallWithValue(target, data, value, errorMessage);
}
function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "../../GSN/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20 {
using SafeMath for uint256;
using Address for address;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
uint8 private _decimals;
/**
* @dev Sets the values for {name} and {symbol}, initializes {decimals} with
* a default value of 18.
*
* To select a different value for {decimals}, use {_setupDecimals}.
*
* All three of these values are immutable: they can only be set once during
* construction.
*/
constructor (string memory name, string memory symbol) {
_name = name;
_symbol = symbol;
_decimals = 18;
}
/**
* @dev Returns the name of the token.
*/
function name() public view returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5,05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
* called.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view returns (uint8) {
return _decimals;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20};
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for ``sender``'s tokens of at least
* `amount`.
*/
function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements
*
* - `to` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Sets {decimals} to a value other than the default one of 18.
*
* WARNING: This function should only be called from the constructor. Most
* applications that interact with token contracts will not expect
* {decimals} to ever change, and may work incorrectly if it does.
*/
function _setupDecimals(uint8 decimals_) internal {
_decimals = decimals_;
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be to transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
import "./interfaces/ICMCDeposit.sol";
import "./CMCCore.sol";
import "./CMCAsset.sol";
import "./lib/LibAsset.sol";
import "./lib/LibERC20.sol";
/// @title CMCDeposit
/// @author Connext <support@connext.network>
/// @notice Contains logic supporting channel multisig deposits. Channel
/// funding is asymmetric, with `alice` having to call a deposit
/// function which tracks the total amount she has deposited so far,
/// and any other funds in the multisig being attributed to `bob`.
contract CMCDeposit is CMCCore, CMCAsset, ICMCDeposit {
mapping(address => uint256) private depositsAlice;
receive() external payable onlyViaProxy nonReentrant {}
function getTotalDepositsAlice(address assetId)
external
view
override
onlyViaProxy
nonReentrantView
returns (uint256)
{
return _getTotalDepositsAlice(assetId);
}
function _getTotalDepositsAlice(address assetId)
internal
view
returns (uint256)
{
return depositsAlice[assetId];
}
function getTotalDepositsBob(address assetId)
external
view
override
onlyViaProxy
nonReentrantView
returns (uint256)
{
return _getTotalDepositsBob(assetId);
}
// Calculated using invariant onchain properties. Note we DONT use safemath here
function _getTotalDepositsBob(address assetId)
internal
view
returns (uint256)
{
return
LibAsset.getOwnBalance(assetId) +
totalTransferred[assetId] -
depositsAlice[assetId];
}
function depositAlice(address assetId, uint256 amount)
external
payable
override
onlyViaProxy
nonReentrant
{
if (LibAsset.isEther(assetId)) {
require(msg.value == amount, "CMCDeposit: VALUE_MISMATCH");
} else {
// If ETH is sent along, it will be attributed to bob
require(msg.value == 0, "CMCDeposit: ETH_WITH_ERC_TRANSFER");
require(
LibERC20.transferFrom(
assetId,
msg.sender,
address(this),
amount
),
"CMCDeposit: ERC20_TRANSFER_FAILED"
);
}
// NOTE: explicitly do NOT use safemath here
depositsAlice[assetId] += amount;
emit AliceDeposited(assetId, amount);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
import "./interfaces/ICMCCore.sol";
import "./ReentrancyGuard.sol";
/// @title CMCCore
/// @author Connext <support@connext.network>
/// @notice Contains logic pertaining to the participants of a channel,
/// including setting and retrieving the participants and the
/// mastercopy.
contract CMCCore is ReentrancyGuard, ICMCCore {
address private immutable mastercopyAddress;
address internal alice;
address internal bob;
/// @notice Set invalid participants to block the mastercopy from being used directly
/// Nonzero address also prevents the mastercopy from being setup
/// Only setting alice is sufficient, setting bob too wouldn't change anything
constructor() {
mastercopyAddress = address(this);
}
// Prevents us from calling methods directly from the mastercopy contract
modifier onlyViaProxy {
require(
address(this) != mastercopyAddress,
"Mastercopy: ONLY_VIA_PROXY"
);
_;
}
/// @notice Contract constructor for Proxied copies
/// @param _alice: Address representing user with function deposit
/// @param _bob: Address representing user with multisig deposit
function setup(address _alice, address _bob)
external
override
onlyViaProxy
{
require(alice == address(0), "CMCCore: ALREADY_SETUP");
require(
_alice != address(0) && _bob != address(0),
"CMCCore: INVALID_PARTICIPANT"
);
require(_alice != _bob, "CMCCore: IDENTICAL_PARTICIPANTS");
ReentrancyGuard.setup();
alice = _alice;
bob = _bob;
}
/// @notice A getter function for the bob of the multisig
/// @return Bob's signer address
function getAlice()
external
view
override
onlyViaProxy
nonReentrantView
returns (address)
{
return alice;
}
/// @notice A getter function for the bob of the multisig
/// @return Alice's signer address
function getBob()
external
view
override
onlyViaProxy
nonReentrantView
returns (address)
{
return bob;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
import "./interfaces/ICMCAsset.sol";
import "./interfaces/Types.sol";
import "./CMCCore.sol";
import "./lib/LibAsset.sol";
import "./lib/LibMath.sol";
import "@openzeppelin/contracts/math/Math.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/// @title CMCAsset
/// @author Connext <support@connext.network>
/// @notice Contains logic to safely transfer channel assets (even if they are
/// noncompliant). During adjudication, balances from defunding the
/// channel or defunding transfers are registered as withdrawable. Once
/// they are registered, the owner (or a watchtower on behalf of the
/// owner), may call `exit` to reclaim funds from the multisig.
contract CMCAsset is CMCCore, ICMCAsset {
using SafeMath for uint256;
using LibMath for uint256;
mapping(address => uint256) internal totalTransferred;
mapping(address => mapping(address => uint256))
private exitableAmount;
function registerTransfer(address assetId, uint256 amount) internal {
totalTransferred[assetId] += amount;
}
function getTotalTransferred(address assetId)
external
view
override
onlyViaProxy
nonReentrantView
returns (uint256)
{
return totalTransferred[assetId];
}
function makeExitable(
address assetId,
address recipient,
uint256 amount
) internal {
exitableAmount[assetId][
recipient
] = exitableAmount[assetId][recipient].satAdd(amount);
}
function makeBalanceExitable(
address assetId,
Balance memory balance
) internal {
for (uint256 i = 0; i < 2; i++) {
uint256 amount = balance.amount[i];
if (amount > 0) {
makeExitable(assetId, balance.to[i], amount);
}
}
}
function getExitableAmount(address assetId, address owner)
external
view
override
onlyViaProxy
nonReentrantView
returns (uint256)
{
return exitableAmount[assetId][owner];
}
function getAvailableAmount(address assetId, uint256 maxAmount)
internal
view
returns (uint256)
{
// Taking the min protects against the case where the multisig
// holds less than the amount that is trying to be withdrawn
// while still allowing the total of the funds to be removed
// without the transaction reverting.
return Math.min(maxAmount, LibAsset.getOwnBalance(assetId));
}
function transferAsset(
address assetId,
address payable recipient,
uint256 amount
) internal {
registerTransfer(assetId, amount);
require(
LibAsset.unregisteredTransfer(assetId, recipient, amount),
"CMCAsset: TRANSFER_FAILED"
);
}
function exit(
address assetId,
address owner,
address payable recipient
) external override onlyViaProxy nonReentrant {
// Either the owner must be the recipient, or in control
// of setting the recipient of the funds to whomever they
// choose
require(
msg.sender == owner || owner == recipient,
"CMCAsset: OWNER_MISMATCH"
);
uint256 amount =
getAvailableAmount(
assetId,
exitableAmount[assetId][owner]
);
// Revert if amount is 0
require(amount > 0, "CMCAsset: NO_OP");
// Reduce the amount claimable from the multisig by the owner
exitableAmount[assetId][
owner
] = exitableAmount[assetId][owner].sub(amount);
// Perform transfer
transferAsset(assetId, recipient, amount);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
/// @title CMCWithdraw
/// @author Connext <support@connext.network>
/// @notice A "mutex" reentrancy guard, heavily influenced by OpenZeppelin.
contract ReentrancyGuard {
uint256 private constant OPEN = 1;
uint256 private constant LOCKED = 2;
uint256 public lock;
function setup() internal {
lock = OPEN;
}
modifier nonReentrant() {
require(lock == OPEN, "ReentrancyGuard: REENTRANT_CALL");
lock = LOCKED;
_;
lock = OPEN;
}
modifier nonReentrantView() {
require(lock == OPEN, "ReentrancyGuard: REENTRANT_CALL");
_;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
/// @title LibMath
/// @author Connext <support@connext.network>
/// @notice This library allows functions that would otherwise overflow and
/// revert if SafeMath was used to instead return the UINT_MAX. In the
/// adjudicator, this is used to ensure you can get the majority of
/// funds out in the event your balance > UINT_MAX and there is an
/// onchain dispute.
library LibMath {
/// @dev Returns the maximum uint256 for an addition that would overflow
/// (saturation arithmetic)
function satAdd(uint256 x, uint256 y) internal pure returns (uint256) {
uint256 sum = x + y;
return sum >= x ? sum : type(uint256).max;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow, so we distribute
return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
import "./interfaces/Commitment.sol";
import "./interfaces/ICMCAdjudicator.sol";
import "./interfaces/ITransferDefinition.sol";
import "./interfaces/Types.sol";
import "./CMCCore.sol";
import "./CMCAsset.sol";
import "./CMCDeposit.sol";
import "./lib/LibChannelCrypto.sol";
import "./lib/LibMath.sol";
import "@openzeppelin/contracts/cryptography/MerkleProof.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
/// @title CMCAdjudicator
/// @author Connext <support@connext.network>
/// @notice Contains logic for disputing a single channel and all active
/// transfers associated with the channel. Contains two major phases:
/// (1) consensus: settle on latest channel state
/// (2) defund: remove assets and dispute active transfers
contract CMCAdjudicator is CMCCore, CMCAsset, CMCDeposit, ICMCAdjudicator {
using LibChannelCrypto for bytes32;
using LibMath for uint256;
using SafeMath for uint256;
uint256 private constant INITIAL_DEFUND_NONCE = 1;
ChannelDispute private channelDispute;
mapping(address => uint256) private defundNonces;
mapping(bytes32 => TransferDispute) private transferDisputes;
modifier validateChannel(CoreChannelState calldata ccs) {
require(
ccs.channelAddress == address(this) &&
ccs.alice == alice &&
ccs.bob == bob,
"CMCAdjudicator: INVALID_CHANNEL"
);
_;
}
modifier validateTransfer(CoreTransferState calldata cts) {
require(
cts.channelAddress == address(this),
"CMCAdjudicator: INVALID_TRANSFER"
);
_;
}
function getChannelDispute()
external
view
override
onlyViaProxy
nonReentrantView
returns (ChannelDispute memory)
{
return channelDispute;
}
function getDefundNonce(address assetId)
external
view
override
onlyViaProxy
nonReentrantView
returns (uint256)
{
return defundNonces[assetId];
}
function getTransferDispute(bytes32 transferId)
external
view
override
onlyViaProxy
nonReentrantView
returns (TransferDispute memory)
{
return transferDisputes[transferId];
}
function disputeChannel(
CoreChannelState calldata ccs,
bytes calldata aliceSignature,
bytes calldata bobSignature
) external override onlyViaProxy nonReentrant validateChannel(ccs) {
// Generate hash
bytes32 ccsHash = hashChannelState(ccs);
// Verify Alice's and Bob's signature on the channel state
verifySignaturesOnChannelStateHash(ccs, ccsHash, aliceSignature, bobSignature);
// We cannot dispute a channel in its defund phase
require(!inDefundPhase(), "CMCAdjudicator: INVALID_PHASE");
// New nonce must be strictly greater than the stored one
require(
channelDispute.nonce < ccs.nonce,
"CMCAdjudicator: INVALID_NONCE"
);
if (!inConsensusPhase()) {
// We are not already in a dispute
// Set expiries
// TODO: offchain-ensure that there can't be an overflow
channelDispute.consensusExpiry = block.timestamp.add(ccs.timeout);
channelDispute.defundExpiry = block.timestamp.add(
ccs.timeout.mul(2)
);
}
// Store newer state
channelDispute.channelStateHash = ccsHash;
channelDispute.nonce = ccs.nonce;
channelDispute.merkleRoot = ccs.merkleRoot;
// Emit event
emit ChannelDisputed(msg.sender, ccs, channelDispute);
}
function defundChannel(
CoreChannelState calldata ccs,
address[] calldata assetIds,
uint256[] calldata indices
) external override onlyViaProxy nonReentrant validateChannel(ccs) {
// These checks are not strictly necessary, but it's a bit cleaner this way
require(assetIds.length > 0, "CMCAdjudicator: NO_ASSETS_GIVEN");
require(
indices.length <= assetIds.length,
"CMCAdjudicator: WRONG_ARRAY_LENGTHS"
);
// Verify that the given channel state matches the stored one
require(
hashChannelState(ccs) == channelDispute.channelStateHash,
"CMCAdjudicator: INVALID_CHANNEL_HASH"
);
// We need to be in defund phase for that
require(inDefundPhase(), "CMCAdjudicator: INVALID_PHASE");
// TODO SECURITY: Beware of reentrancy
// TODO: offchain-ensure that all arrays have the same length:
// assetIds, balances, processedDepositsA, processedDepositsB, defundNonces
// Make sure there are no duplicates in the assetIds -- duplicates are often a source of double-spends
// Defund all assets given
for (uint256 i = 0; i < assetIds.length; i++) {
address assetId = assetIds[i];
// Verify or find the index of the assetId in the ccs.assetIds
uint256 index;
if (i < indices.length) {
// The index was supposedly given -- we verify
index = indices[i];
require(
assetId == ccs.assetIds[index],
"CMCAdjudicator: INDEX_MISMATCH"
);
} else {
// we search through the assets in ccs
for (index = 0; index < ccs.assetIds.length; index++) {
if (assetId == ccs.assetIds[index]) {
break;
}
}
}
// Now, if `index` is equal to the number of assets in ccs,
// then the current asset is not in ccs;
// otherwise, `index` is the index in ccs for the current asset
// Check the assets haven't already been defunded + update the
// defundNonce for that asset
{
// Open a new block to avoid "stack too deep" error
uint256 defundNonce =
(index == ccs.assetIds.length)
? INITIAL_DEFUND_NONCE
: ccs.defundNonces[index];
require(
defundNonces[assetId] < defundNonce,
"CMCAdjudicator: CHANNEL_ALREADY_DEFUNDED"
);
defundNonces[assetId] = defundNonce;
}
// Get total deposits
uint256 tdAlice = _getTotalDepositsAlice(assetId);
uint256 tdBob = _getTotalDepositsBob(assetId);
Balance memory balance;
if (index == ccs.assetIds.length) {
// The current asset is not a part of ccs; refund what has been deposited
balance = Balance({
amount: [tdAlice, tdBob],
to: [payable(ccs.alice), payable(ccs.bob)]
});
} else {
// Start with the final balances in ccs
balance = ccs.balances[index];
// Add unprocessed deposits
balance.amount[0] = balance.amount[0].satAdd(
tdAlice - ccs.processedDepositsA[index]
);
balance.amount[1] = balance.amount[1].satAdd(
tdBob - ccs.processedDepositsB[index]
);
}
// Add result to exitable amounts
makeBalanceExitable(assetId, balance);
}
emit ChannelDefunded(
msg.sender,
ccs,
channelDispute,
assetIds
);
}
function disputeTransfer(
CoreTransferState calldata cts,
bytes32[] calldata merkleProofData
) external override onlyViaProxy nonReentrant validateTransfer(cts) {
// Verify that the given transfer state is included in the "finalized" channel state
bytes32 transferStateHash = hashTransferState(cts);
verifyMerkleProof(
merkleProofData,
channelDispute.merkleRoot,
transferStateHash
);
// The channel needs to be in defund phase for that, i.e. channel state is "finalized"
require(inDefundPhase(), "CMCAdjudicator: INVALID_PHASE");
// Get stored dispute for this transfer
TransferDispute storage transferDispute =
transferDisputes[cts.transferId];
// Verify that this transfer has not been disputed before
require(
transferDispute.transferDisputeExpiry == 0,
"CMCAdjudicator: TRANSFER_ALREADY_DISPUTED"
);
// Store transfer state and set expiry
transferDispute.transferStateHash = transferStateHash;
// TODO: offchain-ensure that there can't be an overflow
transferDispute.transferDisputeExpiry = block.timestamp.add(
cts.transferTimeout
);
emit TransferDisputed(
msg.sender,
cts,
transferDispute
);
}
function defundTransfer(
CoreTransferState calldata cts,
bytes calldata encodedInitialTransferState,
bytes calldata encodedTransferResolver,
bytes calldata responderSignature
) external override onlyViaProxy nonReentrant validateTransfer(cts) {
// Get stored dispute for this transfer
TransferDispute storage transferDispute =
transferDisputes[cts.transferId];
// Verify that a dispute for this transfer has already been started
require(
transferDispute.transferDisputeExpiry != 0,
"CMCAdjudicator: TRANSFER_NOT_DISPUTED"
);
// Verify that the given transfer state matches the stored one
require(
hashTransferState(cts) == transferDispute.transferStateHash,
"CMCAdjudicator: INVALID_TRANSFER_HASH"
);
// We can't defund twice
require(
!transferDispute.isDefunded,
"CMCAdjudicator: TRANSFER_ALREADY_DEFUNDED"
);
transferDispute.isDefunded = true;
Balance memory balance;
if (block.timestamp < transferDispute.transferDisputeExpiry) {
// Ensure the correct hash is provided
require(
keccak256(encodedInitialTransferState) == cts.initialStateHash,
"CMCAdjudicator: INVALID_TRANSFER_HASH"
);
// Before dispute expiry, responder or responder-authorized
// agent (i.e. watchtower) can resolve
require(
msg.sender == cts.responder || cts.initialStateHash.checkSignature(responderSignature, cts.responder),
"CMCAdjudicator: INVALID_RESOLVER"
);
ITransferDefinition transferDefinition =
ITransferDefinition(cts.transferDefinition);
balance = transferDefinition.resolve(
abi.encode(cts.balance),
encodedInitialTransferState,
encodedTransferResolver
);
// Verify that returned balances don't exceed initial balances
require(
balance.amount[0].add(balance.amount[1]) <=
cts.balance.amount[0].add(cts.balance.amount[1]),
"CMCAdjudicator: INVALID_BALANCES"
);
} else {
// After dispute expiry, if the responder hasn't resolved, we defund the initial balance
balance = cts.balance;
}
// Depending on previous code path, defund either resolved or initial balance
makeBalanceExitable(cts.assetId, balance);
// Emit event
emit TransferDefunded(
msg.sender,
cts,
transferDispute,
encodedInitialTransferState,
encodedTransferResolver,
balance
);
}
function verifySignaturesOnChannelStateHash(
CoreChannelState calldata ccs,
bytes32 ccsHash,
bytes calldata aliceSignature,
bytes calldata bobSignature
) internal pure {
bytes32 commitment =
keccak256(abi.encode(CommitmentType.ChannelState, ccsHash));
require(
commitment.checkSignature(aliceSignature, ccs.alice),
"CMCAdjudicator: INVALID_ALICE_SIG"
);
require(
commitment.checkSignature(bobSignature, ccs.bob),
"CMCAdjudicator: INVALID_BOB_SIG"
);
}
function verifyMerkleProof(
bytes32[] calldata proof,
bytes32 root,
bytes32 leaf
) internal pure {
require(
MerkleProof.verify(proof, root, leaf),
"CMCAdjudicator: INVALID_MERKLE_PROOF"
);
}
function inConsensusPhase() internal view returns (bool) {
return block.timestamp < channelDispute.consensusExpiry;
}
function inDefundPhase() internal view returns (bool) {
return
channelDispute.consensusExpiry <= block.timestamp &&
block.timestamp < channelDispute.defundExpiry;
}
function hashChannelState(CoreChannelState calldata ccs)
internal
pure
returns (bytes32)
{
return keccak256(abi.encode(ccs));
}
function hashTransferState(CoreTransferState calldata cts)
internal
pure
returns (bytes32)
{
return keccak256(abi.encode(cts));
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
enum CommitmentType {ChannelState, WithdrawData}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
import "./ITransferRegistry.sol";
import "./Types.sol";
interface ITransferDefinition {
// Validates the initial state of the transfer.
// Called by validator.ts during `create` updates.
function create(bytes calldata encodedBalance, bytes calldata)
external
view
returns (bool);
// Performs a state transition to resolve a transfer and returns final balances.
// Called by validator.ts during `resolve` updates.
function resolve(
bytes calldata encodedBalance,
bytes calldata,
bytes calldata
) external view returns (Balance memory);
// Should also have the following properties:
// string public constant override Name = "...";
// string public constant override StateEncoding = "...";
// string public constant override ResolverEncoding = "...";
// These properties are included on the transfer specifically
// to make it easier for implementers to add new transfers by
// only include a `.sol` file
function Name() external view returns (string memory);
function StateEncoding() external view returns (string memory);
function ResolverEncoding() external view returns (string memory);
function EncodedCancel() external view returns (bytes memory);
function getRegistryInformation()
external
view
returns (RegisteredTransfer memory);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts/cryptography/ECDSA.sol";
/// @author Connext <support@connext.network>
/// @notice This library contains helpers for recovering signatures from a
/// Vector commitments. Channels do not allow for arbitrary signing of
/// messages to prevent misuse of private keys by injected providers,
/// and instead only sign messages with a Vector channel prefix.
library LibChannelCrypto {
function checkSignature(
bytes32 hash,
bytes memory signature,
address allegedSigner
) internal pure returns (bool) {
return recoverChannelMessageSigner(hash, signature) == allegedSigner;
}
function recoverChannelMessageSigner(bytes32 hash, bytes memory signature)
internal
pure
returns (address)
{
bytes32 digest = toChannelSignedMessage(hash);
return ECDSA.recover(digest, signature);
}
function toChannelSignedMessage(bytes32 hash)
internal
pure
returns (bytes32)
{
// 32 is the length in bytes of hash,
// enforced by the type signature above
return
keccak256(abi.encodePacked("\x16Vector Signed Message:\n32", hash));
}
function checkUtilitySignature(
bytes32 hash,
bytes memory signature,
address allegedSigner
) internal pure returns (bool) {
return recoverChannelMessageSigner(hash, signature) == allegedSigner;
}
function recoverUtilityMessageSigner(bytes32 hash, bytes memory signature)
internal
pure
returns (address)
{
bytes32 digest = toUtilitySignedMessage(hash);
return ECDSA.recover(digest, signature);
}
function toUtilitySignedMessage(bytes32 hash)
internal
pure
returns (bytes32)
{
// 32 is the length in bytes of hash,
// enforced by the type signature above
return
keccak256(abi.encodePacked("\x17Utility Signed Message:\n32", hash));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @dev These functions deal with verification of Merkle trees (hash trees),
*/
library MerkleProof {
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
bytes32 proofElement = proof[i];
if (computedHash <= proofElement) {
// Hash(current computed hash + current element of the proof)
computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
} else {
// Hash(current element of the proof + current computed hash)
computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
}
}
// Check if the computed hash (root) is equal to the provided root
return computedHash == root;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental "ABIEncoderV2";
struct RegisteredTransfer {
string name;
address definition;
string stateEncoding;
string resolverEncoding;
bytes encodedCancel;
}
interface ITransferRegistry {
event TransferAdded(RegisteredTransfer transfer);
event TransferRemoved(RegisteredTransfer transfer);
// Should add a transfer definition to the registry
// onlyOwner
function addTransferDefinition(RegisteredTransfer memory transfer) external;
// Should remove a transfer definition to the registry
// onlyOwner
function removeTransferDefinition(string memory name) external;
// Should return all transfer defintions in registry
function getTransferDefinitions()
external
view
returns (RegisteredTransfer[] memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. 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.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
// Check the signature length
if (signature.length != 65) {
revert("ECDSA: invalid signature length");
}
// Divide the signature in r, s and v variables
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
// solhint-disable-next-line no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
// 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 (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): 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) {
revert("ECDSA: invalid signature 's' value");
}
if (v != 27 && v != 28) {
revert("ECDSA: invalid signature 'v' value");
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
require(signer != address(0), "ECDSA: invalid signature");
return signer;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* replicates the behavior of the
* https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
* JSON-RPC method.
*
* 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));
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
import "./interfaces/Commitment.sol";
import "./interfaces/ICMCWithdraw.sol";
import "./interfaces/WithdrawHelper.sol";
import "./CMCCore.sol";
import "./CMCAsset.sol";
import "./lib/LibAsset.sol";
import "./lib/LibChannelCrypto.sol";
import "./lib/LibUtils.sol";
/// @title CMCWithdraw
/// @author Connext <support@connext.network>
/// @notice Contains logic for all cooperative channel multisig withdrawals.
/// Cooperative withdrawal commitments must be signed by both channel
/// participants. As part of the channel withdrawals, an arbitrary
/// call can be made, which is extracted from the withdraw data.
contract CMCWithdraw is CMCCore, CMCAsset, ICMCWithdraw {
using LibChannelCrypto for bytes32;
mapping(bytes32 => bool) private isExecuted;
modifier validateWithdrawData(WithdrawData calldata wd) {
require(
wd.channelAddress == address(this),
"CMCWithdraw: CHANNEL_MISMATCH"
);
_;
}
function getWithdrawalTransactionRecord(WithdrawData calldata wd)
external
view
override
onlyViaProxy
nonReentrantView
returns (bool)
{
return isExecuted[hashWithdrawData(wd)];
}
/// @param wd The withdraw data consisting of
/// semantic withdraw information, i.e. assetId, recipient, and amount;
/// information to make an optional call in addition to the actual transfer,
/// i.e. target address for the call and call payload;
/// additional information, i.e. channel address and nonce.
/// @param aliceSignature Signature of owner a
/// @param bobSignature Signature of owner b
function withdraw(
WithdrawData calldata wd,
bytes calldata aliceSignature,
bytes calldata bobSignature
) external override onlyViaProxy nonReentrant validateWithdrawData(wd) {
// Generate hash
bytes32 wdHash = hashWithdrawData(wd);
// Verify Alice's and Bob's signature on the withdraw data
verifySignaturesOnWithdrawDataHash(wdHash, aliceSignature, bobSignature);
// Replay protection
require(!isExecuted[wdHash], "CMCWithdraw: ALREADY_EXECUTED");
isExecuted[wdHash] = true;
// Determine actually transferable amount
uint256 actualAmount = getAvailableAmount(wd.assetId, wd.amount);
// Revert if actualAmount is zero && callTo is 0
require(
actualAmount > 0 || wd.callTo != address(0),
"CMCWithdraw: NO_OP"
);
// Register and execute the transfer
transferAsset(wd.assetId, wd.recipient, actualAmount);
// Do we have to make a call in addition to the actual transfer?
if (wd.callTo != address(0)) {
WithdrawHelper(wd.callTo).execute(wd, actualAmount);
}
}
function verifySignaturesOnWithdrawDataHash(
bytes32 wdHash,
bytes calldata aliceSignature,
bytes calldata bobSignature
) internal view {
bytes32 commitment =
keccak256(abi.encode(CommitmentType.WithdrawData, wdHash));
require(
commitment.checkSignature(aliceSignature, alice),
"CMCWithdraw: INVALID_ALICE_SIG"
);
require(
commitment.checkSignature(bobSignature, bob),
"CMCWithdraw: INVALID_BOB_SIG"
);
}
function hashWithdrawData(WithdrawData calldata wd)
internal
pure
returns (bytes32)
{
return keccak256(abi.encode(wd));
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.1;
pragma experimental ABIEncoderV2;
import "./ICMCWithdraw.sol";
interface WithdrawHelper {
function execute(WithdrawData calldata wd, uint256 actualAmount) external;
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
}
}Contract ABI
API[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"assetId","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"AliceDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"defunder","type":"address"},{"components":[{"internalType":"address","name":"channelAddress","type":"address"},{"internalType":"address","name":"alice","type":"address"},{"internalType":"address","name":"bob","type":"address"},{"internalType":"address[]","name":"assetIds","type":"address[]"},{"components":[{"internalType":"uint256[2]","name":"amount","type":"uint256[2]"},{"internalType":"address payable[2]","name":"to","type":"address[2]"}],"internalType":"struct Balance[]","name":"balances","type":"tuple[]"},{"internalType":"uint256[]","name":"processedDepositsA","type":"uint256[]"},{"internalType":"uint256[]","name":"processedDepositsB","type":"uint256[]"},{"internalType":"uint256[]","name":"defundNonces","type":"uint256[]"},{"internalType":"uint256","name":"timeout","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"}],"indexed":false,"internalType":"struct ICMCAdjudicator.CoreChannelState","name":"state","type":"tuple"},{"components":[{"internalType":"bytes32","name":"channelStateHash","type":"bytes32"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"consensusExpiry","type":"uint256"},{"internalType":"uint256","name":"defundExpiry","type":"uint256"}],"indexed":false,"internalType":"struct ICMCAdjudicator.ChannelDispute","name":"dispute","type":"tuple"},{"indexed":false,"internalType":"address[]","name":"assetIds","type":"address[]"}],"name":"ChannelDefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"disputer","type":"address"},{"components":[{"internalType":"address","name":"channelAddress","type":"address"},{"internalType":"address","name":"alice","type":"address"},{"internalType":"address","name":"bob","type":"address"},{"internalType":"address[]","name":"assetIds","type":"address[]"},{"components":[{"internalType":"uint256[2]","name":"amount","type":"uint256[2]"},{"internalType":"address payable[2]","name":"to","type":"address[2]"}],"internalType":"struct Balance[]","name":"balances","type":"tuple[]"},{"internalType":"uint256[]","name":"processedDepositsA","type":"uint256[]"},{"internalType":"uint256[]","name":"processedDepositsB","type":"uint256[]"},{"internalType":"uint256[]","name":"defundNonces","type":"uint256[]"},{"internalType":"uint256","name":"timeout","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"}],"indexed":false,"internalType":"struct ICMCAdjudicator.CoreChannelState","name":"state","type":"tuple"},{"components":[{"internalType":"bytes32","name":"channelStateHash","type":"bytes32"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"consensusExpiry","type":"uint256"},{"internalType":"uint256","name":"defundExpiry","type":"uint256"}],"indexed":false,"internalType":"struct ICMCAdjudicator.ChannelDispute","name":"dispute","type":"tuple"}],"name":"ChannelDisputed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"defunder","type":"address"},{"components":[{"internalType":"address","name":"channelAddress","type":"address"},{"internalType":"bytes32","name":"transferId","type":"bytes32"},{"internalType":"address","name":"transferDefinition","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"responder","type":"address"},{"internalType":"address","name":"assetId","type":"address"},{"components":[{"internalType":"uint256[2]","name":"amount","type":"uint256[2]"},{"internalType":"address payable[2]","name":"to","type":"address[2]"}],"internalType":"struct Balance","name":"balance","type":"tuple"},{"internalType":"uint256","name":"transferTimeout","type":"uint256"},{"internalType":"bytes32","name":"initialStateHash","type":"bytes32"}],"indexed":false,"internalType":"struct ICMCAdjudicator.CoreTransferState","name":"state","type":"tuple"},{"components":[{"internalType":"bytes32","name":"transferStateHash","type":"bytes32"},{"internalType":"uint256","name":"transferDisputeExpiry","type":"uint256"},{"internalType":"bool","name":"isDefunded","type":"bool"}],"indexed":false,"internalType":"struct ICMCAdjudicator.TransferDispute","name":"dispute","type":"tuple"},{"indexed":false,"internalType":"bytes","name":"encodedInitialState","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"encodedResolver","type":"bytes"},{"components":[{"internalType":"uint256[2]","name":"amount","type":"uint256[2]"},{"internalType":"address payable[2]","name":"to","type":"address[2]"}],"indexed":false,"internalType":"struct Balance","name":"balance","type":"tuple"}],"name":"TransferDefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"disputer","type":"address"},{"components":[{"internalType":"address","name":"channelAddress","type":"address"},{"internalType":"bytes32","name":"transferId","type":"bytes32"},{"internalType":"address","name":"transferDefinition","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"responder","type":"address"},{"internalType":"address","name":"assetId","type":"address"},{"components":[{"internalType":"uint256[2]","name":"amount","type":"uint256[2]"},{"internalType":"address payable[2]","name":"to","type":"address[2]"}],"internalType":"struct Balance","name":"balance","type":"tuple"},{"internalType":"uint256","name":"transferTimeout","type":"uint256"},{"internalType":"bytes32","name":"initialStateHash","type":"bytes32"}],"indexed":false,"internalType":"struct ICMCAdjudicator.CoreTransferState","name":"state","type":"tuple"},{"components":[{"internalType":"bytes32","name":"transferStateHash","type":"bytes32"},{"internalType":"uint256","name":"transferDisputeExpiry","type":"uint256"},{"internalType":"bool","name":"isDefunded","type":"bool"}],"indexed":false,"internalType":"struct ICMCAdjudicator.TransferDispute","name":"dispute","type":"tuple"}],"name":"TransferDisputed","type":"event"},{"inputs":[{"components":[{"internalType":"address","name":"channelAddress","type":"address"},{"internalType":"address","name":"alice","type":"address"},{"internalType":"address","name":"bob","type":"address"},{"internalType":"address[]","name":"assetIds","type":"address[]"},{"components":[{"internalType":"uint256[2]","name":"amount","type":"uint256[2]"},{"internalType":"address payable[2]","name":"to","type":"address[2]"}],"internalType":"struct Balance[]","name":"balances","type":"tuple[]"},{"internalType":"uint256[]","name":"processedDepositsA","type":"uint256[]"},{"internalType":"uint256[]","name":"processedDepositsB","type":"uint256[]"},{"internalType":"uint256[]","name":"defundNonces","type":"uint256[]"},{"internalType":"uint256","name":"timeout","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"}],"internalType":"struct ICMCAdjudicator.CoreChannelState","name":"ccs","type":"tuple"},{"internalType":"address[]","name":"assetIds","type":"address[]"},{"internalType":"uint256[]","name":"indices","type":"uint256[]"}],"name":"defundChannel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"channelAddress","type":"address"},{"internalType":"bytes32","name":"transferId","type":"bytes32"},{"internalType":"address","name":"transferDefinition","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"responder","type":"address"},{"internalType":"address","name":"assetId","type":"address"},{"components":[{"internalType":"uint256[2]","name":"amount","type":"uint256[2]"},{"internalType":"address payable[2]","name":"to","type":"address[2]"}],"internalType":"struct Balance","name":"balance","type":"tuple"},{"internalType":"uint256","name":"transferTimeout","type":"uint256"},{"internalType":"bytes32","name":"initialStateHash","type":"bytes32"}],"internalType":"struct ICMCAdjudicator.CoreTransferState","name":"cts","type":"tuple"},{"internalType":"bytes","name":"encodedInitialTransferState","type":"bytes"},{"internalType":"bytes","name":"encodedTransferResolver","type":"bytes"},{"internalType":"bytes","name":"responderSignature","type":"bytes"}],"name":"defundTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"assetId","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositAlice","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"channelAddress","type":"address"},{"internalType":"address","name":"alice","type":"address"},{"internalType":"address","name":"bob","type":"address"},{"internalType":"address[]","name":"assetIds","type":"address[]"},{"components":[{"internalType":"uint256[2]","name":"amount","type":"uint256[2]"},{"internalType":"address payable[2]","name":"to","type":"address[2]"}],"internalType":"struct Balance[]","name":"balances","type":"tuple[]"},{"internalType":"uint256[]","name":"processedDepositsA","type":"uint256[]"},{"internalType":"uint256[]","name":"processedDepositsB","type":"uint256[]"},{"internalType":"uint256[]","name":"defundNonces","type":"uint256[]"},{"internalType":"uint256","name":"timeout","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"}],"internalType":"struct ICMCAdjudicator.CoreChannelState","name":"ccs","type":"tuple"},{"internalType":"bytes","name":"aliceSignature","type":"bytes"},{"internalType":"bytes","name":"bobSignature","type":"bytes"}],"name":"disputeChannel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"channelAddress","type":"address"},{"internalType":"bytes32","name":"transferId","type":"bytes32"},{"internalType":"address","name":"transferDefinition","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"responder","type":"address"},{"internalType":"address","name":"assetId","type":"address"},{"components":[{"internalType":"uint256[2]","name":"amount","type":"uint256[2]"},{"internalType":"address payable[2]","name":"to","type":"address[2]"}],"internalType":"struct Balance","name":"balance","type":"tuple"},{"internalType":"uint256","name":"transferTimeout","type":"uint256"},{"internalType":"bytes32","name":"initialStateHash","type":"bytes32"}],"internalType":"struct ICMCAdjudicator.CoreTransferState","name":"cts","type":"tuple"},{"internalType":"bytes32[]","name":"merkleProofData","type":"bytes32[]"}],"name":"disputeTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"assetId","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address payable","name":"recipient","type":"address"}],"name":"exit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAlice","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBob","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChannelDispute","outputs":[{"components":[{"internalType":"bytes32","name":"channelStateHash","type":"bytes32"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"consensusExpiry","type":"uint256"},{"internalType":"uint256","name":"defundExpiry","type":"uint256"}],"internalType":"struct ICMCAdjudicator.ChannelDispute","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"assetId","type":"address"}],"name":"getDefundNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"assetId","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"getExitableAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"assetId","type":"address"}],"name":"getTotalDepositsAlice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"assetId","type":"address"}],"name":"getTotalDepositsBob","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"assetId","type":"address"}],"name":"getTotalTransferred","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"transferId","type":"bytes32"}],"name":"getTransferDispute","outputs":[{"components":[{"internalType":"bytes32","name":"transferStateHash","type":"bytes32"},{"internalType":"uint256","name":"transferDisputeExpiry","type":"uint256"},{"internalType":"bool","name":"isDefunded","type":"bool"}],"internalType":"struct ICMCAdjudicator.TransferDispute","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"channelAddress","type":"address"},{"internalType":"address","name":"assetId","type":"address"},{"internalType":"address payable","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct WithdrawData","name":"wd","type":"tuple"}],"name":"getWithdrawalTransactionRecord","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_alice","type":"address"},{"internalType":"address","name":"_bob","type":"address"}],"name":"setup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"channelAddress","type":"address"},{"internalType":"address","name":"assetId","type":"address"},{"internalType":"address payable","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct WithdrawData","name":"wd","type":"tuple"},{"internalType":"bytes","name":"aliceSignature","type":"bytes"},{"internalType":"bytes","name":"bobSignature","type":"bytes"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.