Contract Source Code:
File 1 of 1 : EthEscrow
// File: openzeppelin-solidity/contracts/math/SafeMath.sol
pragma solidity ^0.5.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) {
require(b <= a, "SafeMath: subtraction overflow");
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-solidity/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) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "SafeMath: division by zero");
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) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
// File: contracts/IEscrow.sol
pragma solidity ^0.5.0;
interface IEscrow {
function balance() external returns (uint);
function send(address payable addr, uint amt) external returns (bool);
}
// File: contracts/Escrow.sol
pragma solidity ^0.5.0;
/**
* Thin wrapper around a ETH/ERC20 payment channel deposit that is controlled
* by a library contract for the purpose of trading with atomic swaps using the
* Arwen protocol.
* @dev Abstract contract with `balance` and `send` methods that must be implemented
* for either ETH or ERC20 tokens in derived contracts. The `send` method should only
* callable by the library contract that controls this escrow
*/
contract Escrow is IEscrow {
address public escrowLibrary;
modifier onlyLibrary() {
require(msg.sender == escrowLibrary, "Only callable by library contract");
_;
}
constructor(address _escrowLibrary) internal {
escrowLibrary = _escrowLibrary;
}
}
/**
* Escrow Contract backed by ETH
*/
contract EthEscrow is Escrow {
constructor(address escrowLibrary) public Escrow(escrowLibrary) {}
function send(address payable addr, uint amt) public onlyLibrary returns (bool) {
return addr.send(amt);
}
function balance() public returns (uint) {
return address(this).balance;
}
}
// File: openzeppelin-solidity/contracts/cryptography/ECDSA.sol
pragma solidity ^0.5.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.
*
* (.note) This call _does not revert_ if the signature is invalid, or
* if the signer is otherwise unable to be retrieved. In those scenarios,
* the zero address is returned.
*
* (.warning) `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) {
return (address(0));
}
// 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) {
return address(0);
}
if (v != 27 && v != 28) {
return address(0);
}
// If the signature is valid (and not malleable), return the signer address
return ecrecover(hash, v, r, s);
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* replicates the behavior of the
* [`eth_sign`](https://github.com/ethereum/wiki/wiki/JSON-RPC#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));
}
}
// File: contracts/EscrowLibrary.sol
pragma solidity ^0.5.0;
/**
* Central contract containing the business logic for interacting with and
* managing the state of Arwen unidirectional payment channels
* @dev Escrows contracts are created and linked to this library from the
* EscrowFactory contract
*/
contract EscrowLibrary {
using SafeMath for uint;
string constant SIGNATURE_PREFIX = '\x19Ethereum Signed Message:\n';
uint constant FORCE_REFUND_TIME = 2 days;
/**
* Escrow State Machine
* @param None Preliminary state of an escrow before it has been created.
* @param Unfunded Initial state of the escrow once created. The escrow can only
* transition to the Open state once it has been funded with required escrow
* amount and openEscrow method is called.
* @param Open From this state the escrow can transition to Closed state
* via the cashout or refund methods or it can transition to PuzzlePosted state
* via the postPuzzle method.
* @param PuzzlePosted From this state the escrow can only transition to
* closed via the solve or puzzleRefund methods
* @param Closed The final sink state of the escrow
*/
enum EscrowState {
None,
Unfunded,
Open,
PuzzlePosted,
Closed
}
/**
* Unique ID for each different type of signed message in the protocol
*/
enum MessageTypeId {
None,
Cashout,
Puzzle,
Refund
}
/**
* Possible reasons the escrow can become closed
*/
enum EscrowCloseReason {
Refund,
PuzzleRefund,
PuzzleSolve,
Cashout,
ForceRefund
}
event PuzzlePosted(address indexed escrow, bytes32 puzzleSighash);
event Preimage(address indexed escrow, bytes32 preimage, bytes32 puzzleSighash);
event EscrowClosed(address indexed escrow, EscrowCloseReason reason, bytes32 closingSighash);
event FundsTransferred(address indexed escrow, address reserveAddress);
struct EscrowParams {
// The amount expected to be funded by the escrower to open the payment channel
uint escrowAmount;
// Expiration time of the escrow when it can refunded by the escrower
uint escrowTimelock;
// Escrower's pub keys
address payable escrowerReserve;
address escrowerTrade;
address escrowerRefund;
// Payee's pub keys
address payable payeeReserve;
address payeeTrade;
// Current state of the escrow
EscrowState escrowState;
// Internal payee/escrower balances within the payment channel
uint escrowerBalance;
uint payeeBalance;
}
/**
* Represents a trade in the payment channel that can be executed
* on-chain by the payee by revealing a hash preimage
*/
struct PuzzleParams {
// The amount of coins in this trade
uint tradeAmount;
// A hash output or "puzzle" which can be "solved" by revealing the preimage
bytes32 puzzle;
// The expiration time of the puzzle when the trade can be refunded by the escrower
uint puzzleTimelock;
// The signature hash of the `postPuzzle` message
bytes32 puzzleSighash;
}
// The EscrowFactory contract that deployed this library
address public escrowFactory;
// Mapping of escrow address to EscrowParams
mapping(address => EscrowParams) public escrows;
// Mapping of escrow address to PuzzleParams
// Only a single puzzle can be posted for a given escrow
mapping(address => PuzzleParams) public puzzles;
constructor() public {
escrowFactory = msg.sender;
}
modifier onlyFactory() {
require(msg.sender == escrowFactory, "Can only be called by escrow factory");
_;
}
/**
* Add a new escrow that is controlled by the library
* @dev Only callable by the factory which should have already deployed the
* escrow at the provided address
*/
function newEscrow(
address escrowAddress,
uint escrowAmount,
uint timelock,
address payable escrowerReserve,
address escrowerTrade,
address escrowerRefund,
address payable payeeReserve,
address payeeTrade
)
public
onlyFactory
{
require(escrows[escrowAddress].escrowState == EscrowState.None, "Escrow already exists");
require(escrowAmount > 0, "Escrow amount too low");
uint escrowerStartingBalance = 0;
uint payeeStartingBalance = 0;
escrows[escrowAddress] = EscrowParams(
escrowAmount,
timelock,
escrowerReserve,
escrowerTrade,
escrowerRefund,
payeeReserve,
payeeTrade,
EscrowState.Unfunded,
escrowerStartingBalance,
payeeStartingBalance
);
EscrowParams storage escrowParams = escrows[escrowAddress];
IEscrow escrow = IEscrow(escrowAddress);
uint escrowBalance = escrow.balance();
// Check the escrow is funded for at least escrowAmount
require(escrowBalance >= escrowAmount, "Escrow not funded");
escrowParams.escrowState = EscrowState.Open;
// If over-funded return any excess funds back to the escrower
if(escrowBalance > escrowAmount) {
escrow.send(escrowParams.escrowerReserve, escrowBalance.sub(escrowAmount));
}
}
/**
* Cashout the escrow with the final balances after trading
* @dev Must be signed by both the escrower and payee trade keys
* @dev Must be in Open state
* @param amountTraded The total amount traded to the payee
*/
function cashout(
address escrowAddress,
uint amountTraded,
bytes memory eSig,
bytes memory pSig
)
public
{
EscrowParams storage escrowParams = escrows[escrowAddress];
require(escrowParams.escrowState == EscrowState.Open, "Escrow must be in state Open");
// Length of the actual message: 20 + 1 + 32
string memory messageLength = '53';
bytes32 sighash = keccak256(abi.encodePacked(
SIGNATURE_PREFIX,
messageLength,
escrowAddress,
uint8(MessageTypeId.Cashout),
amountTraded
));
// Check signatures
require(verify(sighash, eSig) == escrowParams.escrowerTrade, "Invalid escrower cashout sig");
require(verify(sighash, pSig) == escrowParams.payeeTrade, "Invalid payee cashout sig");
escrowParams.payeeBalance = amountTraded;
escrowParams.escrowerBalance = escrowParams.escrowAmount.sub(amountTraded);
escrowParams.escrowState = EscrowState.Closed;
if(escrowParams.escrowerBalance > 0) sendEscrower(escrowAddress, escrowParams);
if(escrowParams.payeeBalance > 0) sendPayee(escrowAddress, escrowParams);
emit EscrowClosed(escrowAddress, EscrowCloseReason.Cashout, sighash);
}
/**
* Allows the escrower to refund the escrow after the escrow expires
* @dev This is a signed refund because it allows the refunder to
* specify the amount traded in the escrow. This is useful for the escrower to
* benevolently close the escrow with the final balances despite the other
* party being offline
* @dev Must be signed by the escrower refund key
* @dev Must be in Open state
* @param amountTraded The total amount traded to the payee
*/
function refund(address escrowAddress, uint amountTraded, bytes memory eSig) public {
EscrowParams storage escrowParams = escrows[escrowAddress];
require(escrowParams.escrowState == EscrowState.Open, "Escrow must be in state Open");
require(now >= escrowParams.escrowTimelock, "Escrow timelock not reached");
// Length of the actual message: 20 + 1 + 32
string memory messageLength = '53';
bytes32 sighash = keccak256(abi.encodePacked(
SIGNATURE_PREFIX,
messageLength,
escrowAddress,
uint8(MessageTypeId.Refund),
amountTraded
));
// Check signature
require(verify(sighash, eSig) == escrowParams.escrowerRefund, "Invalid escrower sig");
escrowParams.payeeBalance = amountTraded;
escrowParams.escrowerBalance = escrowParams.escrowAmount.sub(amountTraded);
escrowParams.escrowState = EscrowState.Closed;
if(escrowParams.escrowerBalance > 0) sendEscrower(escrowAddress, escrowParams);
if(escrowParams.payeeBalance > 0) sendPayee(escrowAddress, escrowParams);
emit EscrowClosed(escrowAddress, EscrowCloseReason.Refund, sighash);
}
/**
* Allows anyone to refund the escrow back to the escrower without a
* signature after escrowTimelock + FORCE_REFUND_TIME
* @dev This method can be used in the event the escrower's keys are lost
* or if the escrower remains offline for an extended period of time
*/
function forceRefund(address escrowAddress) public {
EscrowParams storage escrowParams = escrows[escrowAddress];
require(escrowParams.escrowState == EscrowState.Open, "Escrow must be in state Open");
require(now >= escrowParams.escrowTimelock + FORCE_REFUND_TIME, "Escrow force refund timelock not reached");
escrowParams.escrowerBalance = IEscrow(escrowAddress).balance();
escrowParams.escrowState = EscrowState.Closed;
if(escrowParams.escrowerBalance > 0) sendEscrower(escrowAddress, escrowParams);
// Use 0x0 as the closing sighash because there is no signature required
emit EscrowClosed(escrowAddress, EscrowCloseReason.ForceRefund, 0x0);
}
/**
* Post a hash puzzle unlocks lastest trade in the escrow
* @dev Must be signed by both the escrower and payee trade keys
* @dev Must be in Open state
* @param prevAmountTraded The total amount traded to the payee in the
* payment channel before the last trade
* @param tradeAmount The last trade amount
* @param puzzle A hash puzzle where the solution (preimage) releases the
* `tradeAmount` to the payee
* @param puzzleTimelock The time at which the `tradeAmount` can be
* refunded back to the escrower if the puzzle solution is not posted
*/
function postPuzzle(
address escrowAddress,
uint prevAmountTraded,
uint tradeAmount,
bytes32 puzzle,
uint puzzleTimelock,
bytes memory eSig,
bytes memory pSig
)
public
{
EscrowParams storage escrowParams = escrows[escrowAddress];
require(escrowParams.escrowState == EscrowState.Open, "Escrow must be in state Open");
// Length of the actual message: 20 + 1 + 32 + 32 + 32 + 32
string memory messageLength = '149';
bytes32 sighash = keccak256(abi.encodePacked(
SIGNATURE_PREFIX,
messageLength,
escrowAddress,
uint8(MessageTypeId.Puzzle),
prevAmountTraded,
tradeAmount,
puzzle,
puzzleTimelock
));
require(verify(sighash, eSig) == escrowParams.escrowerTrade, "Invalid escrower sig");
require(verify(sighash, pSig) == escrowParams.payeeTrade, "Invalid payee sig");
puzzles[escrowAddress] = PuzzleParams(
tradeAmount,
puzzle,
puzzleTimelock,
sighash
);
escrowParams.escrowState = EscrowState.PuzzlePosted;
escrowParams.payeeBalance = prevAmountTraded;
escrowParams.escrowerBalance = escrowParams.escrowAmount.sub(prevAmountTraded).sub(tradeAmount);
emit PuzzlePosted(escrowAddress, sighash);
}
/**
* Payee solves the hash puzzle redeeming the last trade amount of funds in the escrow
* @dev Must be in PuzzlePosted state
* @param preimage The preimage x such that H(x) == puzzle
*/
function solvePuzzle(address escrowAddress, bytes32 preimage) public {
EscrowParams storage escrowParams = escrows[escrowAddress];
require(escrowParams.escrowState == EscrowState.PuzzlePosted, "Escrow must be in state PuzzlePosted");
PuzzleParams memory puzzleParams = puzzles[escrowAddress];
bytes32 h = sha256(abi.encodePacked(preimage));
require(h == puzzleParams.puzzle, "Invalid preimage");
emit Preimage(escrowAddress, preimage, puzzleParams.puzzleSighash);
escrowParams.payeeBalance = escrowParams.payeeBalance.add(puzzleParams.tradeAmount);
escrowParams.escrowState = EscrowState.Closed;
emit EscrowClosed(escrowAddress, EscrowCloseReason.PuzzleSolve, puzzleParams.puzzleSighash);
}
/**
* Escrower refunds the last trade amount after `puzzleTimelock` has been reached
* @dev Must be in PuzzlePosted state
*/
function refundPuzzle(address escrowAddress) public {
EscrowParams storage escrowParams = escrows[escrowAddress];
require(escrowParams.escrowState == EscrowState.PuzzlePosted, "Escrow must be in state PuzzlePosted");
PuzzleParams memory puzzleParams = puzzles[escrowAddress];
require(now >= puzzleParams.puzzleTimelock, "Puzzle timelock not reached");
escrowParams.escrowerBalance = escrowParams.escrowerBalance.add(puzzleParams.tradeAmount);
escrowParams.escrowState = EscrowState.Closed;
emit EscrowClosed(escrowAddress, EscrowCloseReason.PuzzleRefund, puzzleParams.puzzleSighash);
}
function withdraw(address escrowAddress, bool escrower) public {
EscrowParams storage escrowParams = escrows[escrowAddress];
require(escrowParams.escrowState == EscrowState.Closed, "Withdraw attempted before escrow is closed");
if(escrower) {
require(escrowParams.escrowerBalance > 0, "escrower balance is 0");
sendEscrower(escrowAddress, escrowParams);
} else {
require(escrowParams.payeeBalance > 0, "payee balance is 0");
sendPayee(escrowAddress, escrowParams);
}
}
function sendEscrower(address escrowAddress, EscrowParams storage escrowParams) internal {
IEscrow escrow = IEscrow(escrowAddress);
uint amountToSend = escrowParams.escrowerBalance;
escrowParams.escrowerBalance = 0;
require(escrow.send(escrowParams.escrowerReserve, amountToSend), "escrower send failure");
emit FundsTransferred(escrowAddress, escrowParams.escrowerReserve);
}
function sendPayee(address escrowAddress, EscrowParams storage escrowParams) internal {
IEscrow escrow = IEscrow(escrowAddress);
uint amountToSend = escrowParams.payeeBalance;
escrowParams.payeeBalance = 0;
require(escrow.send(escrowParams.payeeReserve, amountToSend), "payee send failure");
emit FundsTransferred(escrowAddress, escrowParams.payeeReserve);
}
/**
* Verify a EC signature (v,r,s) on a message digest h
* @return retAddr The recovered address from the signature or 0 if signature is invalid
*/
function verify(bytes32 sighash, bytes memory sig) internal pure returns(address retAddr) {
retAddr = ECDSA.recover(sighash, sig);
}
}
// File: openzeppelin-solidity/contracts/ownership/Ownable.sol
pragma solidity ^0.5.0;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be aplied to your functions to restrict their use to
* the owner.
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* > Note: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
// File: contracts/EscrowFactory.sol
pragma solidity ^0.5.0;
/**
* Creates an EscrowLibrary contract and allows for creating new escrows linked
* to that library
* @dev The factory contract can be self-destructed by the owner to prevent
* new escrows from being created without affecting the library and the ability
* to close already existing escrows
*/
contract EscrowFactory is Ownable {
EscrowLibrary public escrowLibrary;
constructor () public {
escrowLibrary = new EscrowLibrary();
}
event EscrowCreated(
bytes32 indexed escrowParams,
address escrowAddress
);
function createEthEscrow(
uint escrowAmount,
uint timelock,
address payable escrowerReserve,
address escrowerTrade,
address escrowerRefund,
address payable payeeReserve,
address payeeTrade
)
public
{
bytes32 escrowParamsHash = keccak256(abi.encodePacked(
address(this),
escrowAmount,
timelock,
escrowerReserve,
escrowerTrade,
escrowerRefund,
payeeReserve,
payeeTrade
));
bytes memory constructorArgs = abi.encode(address(escrowLibrary));
bytes memory bytecode = abi.encodePacked(type(EthEscrow).creationCode, constructorArgs);
address escrowAddress = createEscrow(bytecode, escrowParamsHash);
escrowLibrary.newEscrow(
escrowAddress,
escrowAmount,
timelock,
escrowerReserve,
escrowerTrade,
escrowerRefund,
payeeReserve,
payeeTrade
);
emit EscrowCreated(escrowParamsHash, escrowAddress);
}
function createEscrow(bytes memory code, bytes32 salt) internal returns (address) {
address addr;
assembly {
addr := create2(0, add(code, 0x20), mload(code), salt)
if iszero(extcodesize(addr)) {
revert(0, 0)
}
}
return addr;
}
}