Source Code
Latest 25 from a total of 7,090 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Underwrite And C... | 13563405 | 387 days ago | IN | 0 ETH | 0.00000658 | ||||
| Underwrite And C... | 13559225 | 387 days ago | IN | 0 ETH | 0.00000218 | ||||
| Underwrite And C... | 13558490 | 387 days ago | IN | 0 ETH | 0.0000011 | ||||
| Underwrite And C... | 13546479 | 387 days ago | IN | 0 ETH | 0.00000047 | ||||
| Underwrite And C... | 13468827 | 389 days ago | IN | 0 ETH | 0.00000207 | ||||
| Underwrite And C... | 13329873 | 392 days ago | IN | 0 ETH | 0.00000164 | ||||
| Underwrite And C... | 13240869 | 394 days ago | IN | 0 ETH | 0.00000629 | ||||
| Underwrite And C... | 13240525 | 394 days ago | IN | 0 ETH | 0.00000883 | ||||
| Underwrite And C... | 13208429 | 395 days ago | IN | 0 ETH | 0.00002544 | ||||
| Underwrite And C... | 13208429 | 395 days ago | IN | 0 ETH | 0.00002544 | ||||
| Underwrite And C... | 13208428 | 395 days ago | IN | 0 ETH | 0.00002567 | ||||
| Underwrite And C... | 13173948 | 396 days ago | IN | 0 ETH | 0.00000179 | ||||
| Underwrite And C... | 13129542 | 397 days ago | IN | 0 ETH | 0.00001015 | ||||
| Underwrite And C... | 13117918 | 397 days ago | IN | 0 ETH | 0.00002371 | ||||
| Underwrite And C... | 13117441 | 397 days ago | IN | 0 ETH | 0.00000818 | ||||
| Underwrite And C... | 13116456 | 397 days ago | IN | 0 ETH | 0.00000128 | ||||
| Underwrite And C... | 13116276 | 397 days ago | IN | 0 ETH | 0.00000101 | ||||
| Underwrite And C... | 13115845 | 397 days ago | IN | 0 ETH | 0.00000118 | ||||
| Underwrite And C... | 12783646 | 405 days ago | IN | 0 ETH | 0.0000045 | ||||
| Underwrite And C... | 12779931 | 405 days ago | IN | 0 ETH | 0.00000632 | ||||
| Underwrite And C... | 12766018 | 405 days ago | IN | 0 ETH | 0.00000619 | ||||
| Underwrite And C... | 12765419 | 405 days ago | IN | 0 ETH | 0.00000527 | ||||
| Underwrite And C... | 12762560 | 405 days ago | IN | 0 ETH | 0.00000309 | ||||
| Underwrite And C... | 12743214 | 406 days ago | IN | 0 ETH | 0.00000121 | ||||
| Underwrite And C... | 12597777 | 409 days ago | IN | 0 ETH | 0.0000032 |
Advanced mode: Intended for advanced users or developers and will display all Internal Transactions including zero value transfers.
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | ||||
|---|---|---|---|---|---|---|---|
| 16264880 | 324 days ago | 0 ETH | |||||
| 16264880 | 324 days ago | 0 ETH | |||||
| 16264880 | 324 days ago | 0 ETH | |||||
| 16237418 | 325 days ago | 0 ETH | |||||
| 16237418 | 325 days ago | 0 ETH | |||||
| 16237418 | 325 days ago | 0 ETH | |||||
| 15838637 | 334 days ago | 0 ETH | |||||
| 15838637 | 334 days ago | 0 ETH | |||||
| 15536637 | 341 days ago | 0 ETH | |||||
| 15536637 | 341 days ago | 0 ETH | |||||
| 15534902 | 341 days ago | 0.00000258 ETH | |||||
| 15534902 | 341 days ago | 0.00000258 ETH | |||||
| 15499949 | 342 days ago | 0 ETH | |||||
| 15499949 | 342 days ago | 0 ETH | |||||
| 15497563 | 342 days ago | 0.0000024 ETH | |||||
| 15497563 | 342 days ago | 0.0000024 ETH | |||||
| 15461549 | 343 days ago | 0 ETH | |||||
| 15461549 | 343 days ago | 0 ETH | |||||
| 15461549 | 343 days ago | 0 ETH | |||||
| 15461353 | 343 days ago | 0 ETH | |||||
| 15461353 | 343 days ago | 0 ETH | |||||
| 15461353 | 343 days ago | 0 ETH | |||||
| 15460398 | 343 days ago | 0 ETH | |||||
| 15460398 | 343 days ago | 0 ETH | |||||
| 15458312 | 343 days ago | 0.00000893 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
CatalystChainInterface
Compiler Version
v0.8.22+commit.4fc1097e
Contract Source Code (Solidity Standard Json-Input format)
//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.19;
import {ERC20} from 'solady/tokens/ERC20.sol';
import {SafeTransferLib} from 'solady/utils/SafeTransferLib.sol';
import { Ownable} from "solady/auth/Ownable.sol";
import { IMessageEscrowStructs } from "GeneralisedIncentives/src/interfaces/IMessageEscrowStructs.sol";
import { IIncentivizedMessageEscrow } from "GeneralisedIncentives/src/interfaces/IIncentivizedMessageEscrow.sol";
import { Bytes65 } from "GeneralisedIncentives/src/utils/Bytes65.sol";
import { ICatalystChainInterface } from "./interfaces/ICatalystChainInterface.sol";
import { ICatalystReceiver } from "./interfaces/IOnCatalyst.sol";
import "./interfaces/ICatalystV1VaultState.sol"; // structs
import { ICatalystV1Vault } from "./ICatalystV1Vault.sol";
import "./ICatalystV1Vault.sol";
import "./CatalystPayload.sol";
/**
* @title Catalyst Cross-chain Interface for Generalised Incentives.
* @author Cata Labs Inc.
* @notice Cross-chain interface built using Generalised Incentives.
* Catalyst vaults depends on a translator contract to execute cross-chain swaps to convert
* the swap details into a bytearray that can be sent to the destination chain.
*
* The cross-chain interface is also in charge of extending messaging protocol with additional features
* like underwriting.
*
* Underwriting is facilitated by allowing external actors to front the output of a swap. The cross-chain interfaces creates
* an appropriate escrow on the vault and then pays the user. Once the incoming swap arrives, it is intercepted to release the
* escrow to then pay the underwriter.
*
* @dev To adopt the cross-chain interface for another messaging protocol, you need to
* - Change the message submission flow: (GARP.submitMessage(...))
* - Change the package callbakcs (receiveMessage, receiveAck)
*
* However, it may be required to also modify the vaults as the incentive structure is currently fixed.
*/
contract CatalystChainInterface is ICatalystChainInterface, Ownable, Bytes65 {
//--- ERRORS ---//
/** @dev Only the message router should be able to deliver messages. */
error InvalidCaller(); // 48f5c3ed
error InvalidContext(bytes1 context); // 9f769791
error InvalidAddress(); // e6c4247b
error InvalidSourceApplication(); // 003923e0
error NotEnoughIncentives(uint256 expected, uint256 actual); // 6de78246
error ChainAlreadySetup(); // b8e35614
//-- Underwriting Errors --//
error SwapAlreadyUnderwritten(); // d0c27c9f
error UnderwriteDoesNotExist(bytes32 identifier); // ae029d69
error UnderwriteNotExpired(uint256 blocksUnitilExpiry); // 62141db5
error MaxUnderwriteDurationTooLong(); // 3f6368aa
error MaxUnderwriteDurationTooShort(); // 6229dcd0
error NoVaultConnection(); // ea66ca6d
error SwapRecentlyUnderwritten(); // 695b3a94
//--- Events ---//
event SwapFailed(bytes1 error);
event RemoteImplementationSet(bytes32 chainIdentifier, bytes remoteCCI, bytes remoteGARP);
event MaxUnderwriteDuration(uint256 newMaxUnderwriteDuration);
event MinGasFor(
bytes32 identifier,
uint48 minGas
);
//-- Underwriting Events --//
event SwapUnderwritten(
bytes32 indexed identifier,
address indexed underwriter,
uint96 expiry,
address targetVault,
address toAsset,
uint256 U,
address toAccount,
uint256 outAmount
);
event FulfillUnderwrite(
bytes32 indexed identifier
);
event ExpireUnderwrite(
bytes32 indexed identifier,
address expirer,
uint256 reward
);
//--- Structs ---//
struct UnderwritingStorage {
uint256 tokens; // 1 slot
address refundTo; // 2 slot: 20/32
uint96 expiry; // 2 slot: 32/32
}
//--- Underwrite Duration Frame Parameters ---//
// Underwriters should take on as little risk as possible. One risk associated with underwriting is
// chain haulting or timestamp manipulation / diviation. To combat this risk, time is measured in blocks.
// This implies that there needs to be some configuration if the block time changes over time or to fit it
// to various blockchains.
// The reason behind is if the underwrite duration was timestamp based, say 8 hours. If the chain haulted for
// 10 hours, then the underwriter would risk being expired and lose everything. If a chain doesn't produce any
// blocks during a hault, then it wouldn't be a risk to an underwriter.
// Generally, it is more common for unpredictable block slowdowns rather than unpredictable block speedups.
/// @notice The initial underwrite duration (in blocks).
/// @dev Is 8 hours if the block time is 2 seconds, 48 hours if the block time is 12 seconds. Not a great initial value but better than nothing.
uint256 constant INITIAL_MAX_UNDERWRITE_BLOCK_DURATION = 24 hours / 6 seconds;
/// @notice The maximum configurable underwrite duration (in blocks).
/// @dev Is 9.3 days if the block time is 2 seconds, 56 days if the block time is 12 seconds.
uint256 constant MAX_UNDERWRITE_BLOCK_DURATION = 14 days / 3 seconds;
/// @notice The minimum configurable underwrite duration (in blocks).
/// @dev Is 10 minutes if the block time is 2 seconds, 1 hour if the block time is 12 seconds.
uint256 constant MIN_UNDERWRITE_BLOCK_DURATION = 1 hours / 12 seconds;
//--- Config ---//
/** @notice The generalised incentives endpoint */
IIncentivizedMessageEscrow public immutable GARP; // Set on deployment
//-- Underwriting Config--//
/**
* @notice How many blocks should there be between when an identifier can be underwritten.
* @dev The purpose of these buffer blocks is to ensure that underwriters don't mistakenly underwrite swaps right after they are filled.
*/
uint24 constant BUFFER_BLOCKS = 4;
/** @notice Set underwriter collateral. Is of UNDERWRITING_COLLATERAL_DENOMINATOR */
uint256 constant public UNDERWRITING_COLLATERAL = 35; // 3.5% extra as collateral.
uint256 constant public UNDERWRITING_COLLATERAL_DENOMINATOR = 1000;
/** @notice How much of the collateral is given to the exipirer the rest goes to the vault. Is of EXPIRE_CALLER_REWARD_DENOMINATOR. */
uint256 constant public EXPIRE_CALLER_REWARD = 350; // 35% of the 3.5% = 1.225%. Of $1000 = $12.25
uint256 constant public EXPIRE_CALLER_REWARD_DENOMINATOR = 1000;
//--- Storage ---//
/** @notice The destination address on the chain by chain identifier. */
mapping(bytes32 => bytes) public chainIdentifierToDestinationAddress;
/** @notice The minimum amount of gas for a specific chain. bytes32(0) indicates ack. */
mapping(bytes32 => uint48) public minGasFor;
//-- Underwriting Storage --//
/**
* @notice Sets the maximum duration for underwriting.
* @dev Should be set long enough for all swaps to be able to confirm + a small buffer
* Should also be set short enough to not take up an excess amount of escrow usage.
*/
uint256 public maxUnderwritingDuration = INITIAL_MAX_UNDERWRITE_BLOCK_DURATION;
/**
* @notice Maps underwriting identifiers to underwriting state.
* refundTo can be checked to see if the ID has been underwritten.
*/
mapping(bytes32 => UnderwritingStorage) public underwritingStorage;
constructor(address GARP_, address defaultOwner) payable {
require(address(GARP_) != address(0)); // dev: GARP_ cannot be zero address
GARP = IIncentivizedMessageEscrow(GARP_);
_initializeOwner(defaultOwner);
emit MaxUnderwriteDuration(INITIAL_MAX_UNDERWRITE_BLOCK_DURATION);
}
//-- Admin--//
/**
* @notice Allow updating of the minimum gas limit.
* @dev Set chainIdentifier to 0 for gas for ack.
*/
function setMinGasFor(bytes32 chainIdentifier, uint48 minGas) override external onlyOwner {
minGasFor[chainIdentifier] = minGas;
emit MinGasFor(chainIdentifier, minGas);
}
/**
* @notice Sets the new max underwrite duration: That is the period of time before an underwrite can be expired.
* When an underwrite is expired, the underwriter loses all capital provided.
* @dev This function can be exploited by the owner. By setting newMaxUnderwriteDuration to (almost) 0 right before
* someone calls underwrite and then expiring them before the actual swap arrives. The min protection here is not
* sufficient since it needs to be well into when a message can be validated. As a result, the owner of this contract
* should be a timelock which underwriters monitor or trusted by underwriters.
*/
function setMaxUnderwritingDuration(uint256 newMaxUnderwriteDuration) onlyOwner override external {
if (newMaxUnderwriteDuration <= MIN_UNDERWRITE_BLOCK_DURATION) revert MaxUnderwriteDurationTooShort();
// If the underwriting duration is too long, users can freeze up a lot of value for not a lot of cost.
if (newMaxUnderwriteDuration > MAX_UNDERWRITE_BLOCK_DURATION) revert MaxUnderwriteDurationTooLong();
maxUnderwritingDuration = newMaxUnderwriteDuration;
emit MaxUnderwriteDuration(newMaxUnderwriteDuration);
}
/**
* @notice Check that the incentives are set. This is important for the system work
* without external monitoring. Especially the ack gas limit is important.
* @dev Is it enforced that the gas price of ack has to be 10% higher than the gas price spent on the submitted transaction.
*/
modifier checkRouteDescription(ICatalystV1Vault.RouteDescription calldata routeDescription) {
// -- Check incentives -- //
ICatalystV1Vault.IncentiveDescription calldata incentive = routeDescription.incentive;
// 1. Gas limits
uint48 minGasChainIdentifier = minGasFor[routeDescription.chainIdentifier];
if (incentive.maxGasDelivery < minGasChainIdentifier) revert NotEnoughIncentives(minGasChainIdentifier, incentive.maxGasDelivery);
uint48 minGasDefault = minGasFor[bytes32(0)];
if (incentive.maxGasAck < minGasDefault) revert NotEnoughIncentives(minGasDefault, incentive.maxGasAck);
// 2. Gas prices
// The gas price of ack has to be 10% higher than the gas price spent on this transaction.
if (incentive.priceOfAckGas < tx.gasprice * 11 / 10) revert NotEnoughIncentives(tx.gasprice * 11 / 10, incentive.priceOfAckGas);
// -- Check Address Lengths -- //
// toAccount
if (!_checkBytes65(routeDescription.toAccount)) revert InvalidBytes65Address();
// toVault
if (!_checkBytes65(routeDescription.toVault)) revert InvalidBytes65Address();
_;
}
modifier onlyGARP() {
if (msg.sender != address(GARP)) revert InvalidCaller();
_;
}
modifier verifySourceChainAddress(bytes32 sourceChainIdentifier, bytes calldata fromApplication) {
if (keccak256(fromApplication) != keccak256(chainIdentifierToDestinationAddress[sourceChainIdentifier])) revert InvalidSourceApplication();
_;
}
//-- Transparent viewer --//
/**
* @notice Estimate the addition verification cost beyond the cost paid to the relayer.
* @dev This is implement as a simple lookup on GARP.
*/
function estimateAdditionalCost() override external view returns(address asset, uint256 amount) {
(asset, amount) = GARP.estimateAdditionalCost();
}
//-- Functions --//
/**
* @notice matches the hash of error calldata to common revert functions
* and then reverts a relevant ack which can be exposed on the origin to provide information
* about why the transaction didn't execute as expected.
* @param err The error in bytes
*/
function _handleError(bytes memory err) pure internal returns (bytes1) {
// To only get the error identifier, only use the first 8 bytes. This lets us add additional error
// data for easier debugger on trace.
bytes8 errorIdentifier = bytes8(err);
// We can use memory slices to get better insight into exactly the error which occured.
// This would also allow us to reuse events.
// However, it looks like it will significantly increase gas costs so this works for now.
// It looks like Solidity will improve their error catch implementation which can replace this.
if (bytes8(abi.encodeWithSelector(ExceedsSecurityLimit.selector)) == errorIdentifier) return 0x11;
if (bytes8(abi.encodeWithSelector(ReturnInsufficient.selector)) == errorIdentifier) return 0x12;
if (bytes8(abi.encodeWithSelector(VaultNotConnected.selector)) == errorIdentifier) return 0x13;
return 0x10; // unknown error.
}
/**
* @notice Connects this CCI with another contract on another chain.
* @dev Each chain can only be setup once. This reduces governance risks.
* @param remoteCCI The bytes65 encoded address on the destination chain.
* @param remoteGARP The messaging router encoded address on the destination chain.
*/
function connectNewChain(bytes32 chainIdentifier, bytes calldata remoteCCI, bytes calldata remoteGARP) onlyOwner checkBytes65Address(remoteCCI) override external {
// Check if the chain has already been set.
// If it has, we don't allow setting it as another. This would impact existing pools.
if (chainIdentifierToDestinationAddress[chainIdentifier].length != 0) revert ChainAlreadySetup();
// Set the remote CCI.
chainIdentifierToDestinationAddress[chainIdentifier] = remoteCCI;
emit RemoteImplementationSet(chainIdentifier, remoteCCI, remoteGARP);
// Set the remote messaging router escrow.
GARP.setRemoteImplementation(chainIdentifier, remoteGARP);
}
/**
* @notice Packs cross-chain swap information into a bytearray to send to the destination cross-chain interface.
* @dev Callable by anyone but this cannot be abused since the connection management ensures no wrong messages enter a healthy vault.
* @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive.
* @param toAssetIndex The index of the asset the user wants to buy in the target vault.
* @param U The calculated liquidity reference. (Units)
* @param minOut The minimum number output of tokens on the target chain.
* @param fromAmount Escrow related value. The amount returned if the swap fails.
* @param fromAsset Escrow related value. The asset that was sold.
* @param underwriteIncentiveX16 The payment for underwriting the swap (out of type(uint16).max)
* @param calldata_ Data field if a call should be made on the target chain.
* Encoding depends on the target chain, with EVM: bytes.concat(bytes20(uint160(<address>)), <data>).
*/
function sendCrossChainAsset(
ICatalystV1Vault.RouteDescription calldata routeDescription,
uint8 toAssetIndex,
uint256 U,
uint256 minOut,
uint256 fromAmount,
address fromAsset,
uint16 underwriteIncentiveX16,
bytes calldata calldata_
) checkRouteDescription(routeDescription) override external payable {
// We need to ensure that all information is in the correct places. This ensures that calls to this contract
// will always be decoded semi-correctly even if the inputs are incorrect. This also checks that the user inputs
// into the swap contracts are correct while making the cross-chain interface flexible for future implementations.
// These checks are done by the modifier.
// Anyone can call this function, but unless someone can also manage to pass the security check on onRecvPacket
// they cannot drain any value. As such, the very worst they can do is waste gas.
// Encode payload. See CatalystPayload.sol for the payload definition
bytes memory data = bytes.concat(
CTX0_ASSET_SWAP,
// bytes.concat(
bytes1(uint8(20)), // EVM addresses are 20 bytes.
bytes32(0), // EVM only uses 20 bytes. abi.encode packs the 20 bytes into 32 then we need to add 32 more
bytes32(uint256(uint160(msg.sender))), // Use abi.encode to encode address into 32 bytes
// ),
bytes.concat(
routeDescription.toVault, // Length is expected to be pre-encoded.
routeDescription.toAccount, // Length is expected to be pre-encoded.
bytes32(U),
bytes1(toAssetIndex),
bytes32(minOut),
bytes32(fromAmount)
),
// bytes.concat(
bytes1(uint8(20)), // EVM addresses are 20 bytes.
bytes32(0), // EVM only uses 20 bytes. abi.encode packs the 20 bytes into 32 then we need to add 32 more
bytes32(uint256(uint160(fromAsset))), // Use abi.encode to encode address into 32 bytes
// ),
bytes4(uint32(block.number)), // This is the same as block.number mod 2**32-1
bytes2(uint16(underwriteIncentiveX16)),
bytes2(uint16(calldata_.length)), // max length of calldata is 2**16-1 = 65535 bytes which should be more than plenty.
calldata_
);
GARP.submitMessage{value: msg.value}(
routeDescription.chainIdentifier,
chainIdentifierToDestinationAddress[routeDescription.chainIdentifier],
data,
routeDescription.incentive,
routeDescription.deadline
);
}
/**
* @notice Packs cross-chain swap information into a bytearray and sends it to the destination cross-chain interface.
* @dev Callable by anyone but this cannot be abused since the connection management ensures no wrong messages enter a healthy vault.
* @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive.
* @param U The calculated liquidity reference. (Units)
* @param minOut An array of minout describing: [the minimum number of vault tokens, the minimum number of reference assets]
* @param fromAmount Escrow related value. The amount returned if the swap fails.
* @param calldata_ Data field if a call should be made on the target chain.
* Encoding depends on the target chain, with EVM: bytes.concat(bytes20(uint160(<address>)), <data>).
*/
function sendCrossChainLiquidity(
ICatalystV1Vault.RouteDescription calldata routeDescription,
uint256 U,
uint256[2] calldata minOut,
uint256 fromAmount,
bytes calldata calldata_
) checkRouteDescription(routeDescription) override external payable {
// We need to ensure that all information is in the correct places. This ensures that calls to this contract
// will always be decoded semi-correctly even if the input is incorrect. This also checks that the user inputs
// into the swap contracts are correct while making the cross-chain interface flexible for future implementations.
// These checks are done by the modifier.
// Anyone can call this function, but unless someone can also manage to pass the security check on onRecvPacket
// they cannot drain any value. As such, the very worst they can do is waste gas.
// Encode payload. See CatalystPayload.sol for the payload definition
bytes memory data = bytes.concat(
CTX1_LIQUIDITY_SWAP,
// bytes.concat(
bytes1(uint8(20)), // EVM addresses are 20 bytes.
bytes32(0), // EVM only uses 20 bytes. abi.encode packs the 20 bytes into 32 then we need to add 32 more
bytes32(uint256(uint160(msg.sender))), // Use abi.encode to encode address into 32 bytes
// )
routeDescription.toVault, // Length is expected to be pre-encoded.
routeDescription.toAccount, // Length is expected to be pre-encoded.
bytes32(U),
bytes32(minOut[0]),
bytes32(minOut[1]),
bytes32(fromAmount),
bytes4(uint32(block.number)),
bytes2(uint16(calldata_.length)),
calldata_
);
GARP.submitMessage{value: msg.value}(
routeDescription.chainIdentifier,
chainIdentifierToDestinationAddress[routeDescription.chainIdentifier],
data,
routeDescription.incentive,
routeDescription.deadline
);
}
/**
* @notice Cross-chain message success handler
* @dev Should never revert. (on valid messages)
*/
function _onPacketSuccess(bytes32 destinationIdentifier, bytes calldata data) internal {
bytes1 context = data[CONTEXT_POS];
// Since this is a callback, fromVault must be an EVM address.
address fromVault = address(bytes20(data[ FROM_VAULT_START_EVM : FROM_VAULT_END ]));
if (context == CTX0_ASSET_SWAP) {
ICatalystV1Vault(fromVault).onSendAssetSuccess(
destinationIdentifier, // connectionId
data[ TO_ACCOUNT_LENGTH_POS : TO_ACCOUNT_END ], // toAccount
uint256(bytes32(data[ UNITS_START : UNITS_END ])), // units
uint256(bytes32(data[ CTX0_FROM_AMOUNT_START : CTX0_FROM_AMOUNT_END ])), // fromAmount
address(bytes20(data[ CTX0_FROM_ASSET_START_EVM : CTX0_FROM_ASSET_END ])), // fromAsset
uint32(bytes4(data[ CTX0_BLOCK_NUMBER_START : CTX0_BLOCK_NUMBER_END ])) // block number
);
return;
}
if (context == CTX1_LIQUIDITY_SWAP) {
ICatalystV1Vault(fromVault).onSendLiquiditySuccess(
destinationIdentifier, // connectionId
data[ TO_ACCOUNT_LENGTH_POS : TO_ACCOUNT_END ], // toAccount
uint256(bytes32(data[ UNITS_START : UNITS_END ])), // units
uint256(bytes32(data[ CTX1_FROM_AMOUNT_START : CTX1_FROM_AMOUNT_END ])), // fromAmount
uint32(bytes4(data[ CTX1_BLOCK_NUMBER_START : CTX1_BLOCK_NUMBER_END ])) // block number
);
return;
}
// A proper message should never get here. If the message got here, we are never going to be able to properly process it.
revert InvalidContext(context);
}
/**
* @notice Cross-chain message failure handler
* @dev Should never revert. (on valid messages)
*/
function _onPacketFailure(bytes32 destinationIdentifier, bytes calldata data) internal {
bytes1 context = data[CONTEXT_POS];
// Since this is a callback, fromVault must be an EVM address.
address fromVault = address(bytes20(data[ FROM_VAULT_START_EVM : FROM_VAULT_END ]));
if (context == CTX0_ASSET_SWAP) {
return ICatalystV1Vault(fromVault).onSendAssetFailure(
destinationIdentifier, // connectionId
data[ TO_ACCOUNT_LENGTH_POS : TO_ACCOUNT_END ], // toAccount
uint256(bytes32(data[ UNITS_START : UNITS_END ])), // units
uint256(bytes32(data[ CTX0_FROM_AMOUNT_START : CTX0_FROM_AMOUNT_END ])), // fromAmount
address(bytes20(data[ CTX0_FROM_ASSET_START_EVM : CTX0_FROM_ASSET_END ])), // fromAsset
uint32(bytes4(data[ CTX0_BLOCK_NUMBER_START : CTX0_BLOCK_NUMBER_END ])) // block number
);
}
if (context == CTX1_LIQUIDITY_SWAP) {
return ICatalystV1Vault(fromVault).onSendLiquidityFailure(
destinationIdentifier, // connectionId
data[ TO_ACCOUNT_LENGTH_POS : TO_ACCOUNT_END ], // toAccount
uint256(bytes32(data[ UNITS_START : UNITS_END ])), // units
uint256(bytes32(data[ CTX1_FROM_AMOUNT_START : CTX1_FROM_AMOUNT_END ])), // fromAmount
uint32(bytes4(data[ CTX1_BLOCK_NUMBER_START : CTX1_BLOCK_NUMBER_END ])) // block number
);
}
// A proper message should never get here. If the message got here, we are never going to be able to properly process it.
revert InvalidContext(context);
}
/**
* @notice The Acknowledgement package handler
* @dev Should never revert. (on valid messages)
* @param destinationIdentifier Identifier for the destination chain
* @param acknowledgement The acknowledgement bytes for the cross-chain swap.
*/
function receiveAck(bytes32 destinationIdentifier, bytes32 /* messageIdentifier */, bytes calldata acknowledgement) onlyGARP override external {
// If the transaction executed but some logic failed, an ack is sent back with an error acknowledgement.
// We refer to this as "fail on ack". The package should be failed.
// The acknowledgement is prepended the message, so we need to fetch it.
// Then, we need to ignore it when passing the data to the handlers.
bytes1 swapStatus = acknowledgement[0];
if (swapStatus != 0x00) {
emit SwapFailed(swapStatus); // The acknowledgement can be mapped to get some information about what happened.
return _onPacketFailure(destinationIdentifier, acknowledgement[1:]);
}
// Otherwise, swapStatus == 0x00 which implies success.
_onPacketSuccess(destinationIdentifier, acknowledgement[1:]);
}
/**
* @notice The receive packet handler
* @param sourceIdentifier Source chain identifier.
* @param fromApplication The bytes65 encoded fromApplication.
* @param message The message sent by the source chain.
* @return acknowledgement The acknowledgement status of the transaction after execution.
*/
function receiveMessage(bytes32 sourceIdentifier, bytes32 /* messageIdentifier */, bytes calldata fromApplication, bytes calldata message) onlyGARP verifySourceChainAddress(sourceIdentifier, fromApplication) override external returns (bytes memory acknowledgement) {
bytes1 swapStatus = _receiveMessage(sourceIdentifier, message);
// We will send the original message back. While this is not ideal from a size perspective, it makes it very easy to manage.
return acknowledgement = bytes.concat(
swapStatus,
message
);
}
/**
* @notice Message handler
* @param data The data packet we originally sent.
* @return acknowledgement The status of the transaction after execution
*/
function _receiveMessage(bytes32 sourceIdentifier, bytes calldata data) internal virtual returns (bytes1 acknowledgement) {
bytes1 context = data[CONTEXT_POS];
// Check that toAccount is the correct length and only contains 0 bytes beyond the address.
if (uint8(data[TO_ACCOUNT_LENGTH_POS]) != 20) revert InvalidAddress(); // Check correct length
if (uint256(bytes32(data[TO_ACCOUNT_START:TO_ACCOUNT_START+32])) != 0) revert InvalidAddress(); // Check first 32 bytes are 0.
if (uint96(bytes12(data[TO_ACCOUNT_START+32:TO_ACCOUNT_START_EVM])) != 0) revert InvalidAddress(); // Check the next 32-20=12 bytes are 0.
// To vault will not be checked. If it is assumed that any error is random, then an incorrect toVault will result in the call failling.
// The reason toAccount is checked is that any to account will be treated as valid. So any random error will result
// in lost funds.
if (context == CTX0_ASSET_SWAP) {
return acknowledgement = _handleReceiveAsset(sourceIdentifier, data);
}
if (context == CTX1_LIQUIDITY_SWAP) {
return acknowledgement = _handleReceiveLiquidity(sourceIdentifier, data);
}
/* revert InvalidContext(context); */
// No revert here. Instead, another implementation can override this implementation. It should just keep adding ifs with returns inside:
// acknowledgement = super._receiveMessage(...)
// if (acknowledgement == 0x01) { if (context == CTXX) ...}
return acknowledgement = 0x01;
}
function _handleReceiveAssetFallback(bytes32 sourceIdentifier, bytes calldata data) internal returns (bytes1 status) {
// We don't know how from_vault is encoded. So we load it as bytes. Including the length.
bytes calldata fromVault = data[ FROM_VAULT_LENGTH_POS : FROM_VAULT_END ];
// We know that toVault is an EVM address
address toVault = address(bytes20(data[ TO_VAULT_START_EVM : TO_VAULT_END ]));
try ICatalystV1Vault(toVault).receiveAsset(
sourceIdentifier, // connectionId
fromVault, // fromVault
uint8(data[CTX0_TO_ASSET_INDEX_POS]), // toAssetIndex
address(bytes20(data[ TO_ACCOUNT_START_EVM : TO_ACCOUNT_END ])), // toAccount
uint256(bytes32(data[ UNITS_START : UNITS_END ])), // units
uint256(bytes32(data[ CTX0_MIN_OUT_START : CTX0_MIN_OUT_END ])), // minOut
uint256(bytes32(data[ CTX0_FROM_AMOUNT_START : CTX0_FROM_AMOUNT_END ])), // fromAmount
bytes(data[ CTX0_FROM_ASSET_LENGTH_POS : CTX0_FROM_ASSET_END ]), // fromAsset
uint32(bytes4(data[ CTX0_BLOCK_NUMBER_START : CTX0_BLOCK_NUMBER_END ])) // blocknumber
) returns(uint256 purchasedTokens) {
uint16 dataLength = uint16(bytes2(data[CTX0_DATA_LENGTH_START : CTX0_DATA_LENGTH_END]));
if (dataLength != 0) {
address dataTarget = address(bytes20(data[ CTX0_DATA_START : CTX0_DATA_START+20 ]));
bytes calldata dataArguments = data[ CTX0_DATA_START+20 : CTX0_DATA_START+dataLength ];
// Let users define custom logic which should be executed after the swap.
// The logic is not contained within a try - except so if the logic reverts
// the transaction will timeout and the user gets the input tokens on the sending chain.
// If this is not desired, wrap further logic in a try - except at dataTarget.
ICatalystReceiver(dataTarget).onCatalystCall(purchasedTokens, dataArguments, false);
// If dataTarget doesn't implement onCatalystCall BUT implements a fallback function, the call will still succeed.
}
return 0x00;
} catch (bytes memory err) {
return _handleError(err);
}
}
function _handleReceiveLiquidity(bytes32 sourceIdentifier, bytes calldata data) internal returns (bytes1 status) {
// We don't know how from_vault is encoded. So we load it as bytes. Including the length.
bytes calldata fromVault = data[ FROM_VAULT_LENGTH_POS : FROM_VAULT_END ];
// We know that toVault is an EVM address
address toVault = address(bytes20(data[ TO_VAULT_START_EVM : TO_VAULT_END ]));
try ICatalystV1Vault(toVault).receiveLiquidity(
sourceIdentifier, // connectionId
fromVault, // fromVault
address(bytes20(data[ TO_ACCOUNT_START_EVM : TO_ACCOUNT_END ])), // toAccount
uint256(bytes32(data[ UNITS_START : UNITS_END ])), // units
uint256(bytes32(data[ CTX1_MIN_VAULT_TOKEN_START : CTX1_MIN_VAULT_TOKEN_END ])), // minOut
uint256(bytes32(data[ CTX1_MIN_REFERENCE_START : CTX1_MIN_REFERENCE_END ])),// minOut
uint256(bytes32(data[ CTX1_FROM_AMOUNT_START : CTX1_FROM_AMOUNT_END ])), // fromAmount
uint32(bytes4(data[ CTX1_BLOCK_NUMBER_START : CTX1_BLOCK_NUMBER_END ])) // blocknumber
) returns (uint256 purchasedVaultTokens) {
uint16 dataLength = uint16(bytes2(data[CTX1_DATA_LENGTH_START : CTX1_DATA_LENGTH_END]));
if (dataLength != 0) {
address dataTarget = address(bytes20(data[ CTX1_DATA_START : CTX1_DATA_START+20 ]));
bytes calldata dataArguments = data[ CTX1_DATA_START+20 : CTX1_DATA_START+dataLength ];
// Let users define custom logic which should be executed after the swap.
// The logic is not contained within a try - except so if the logic reverts
// the transaction will timeout and the user gets the input tokens on the sending chain.
// If this is not desired, wrap further logic in a try - except at dataTarget.
ICatalystReceiver(dataTarget).onCatalystCall(purchasedVaultTokens, dataArguments, false);
// If dataTarget doesn't implement onCatalystCall BUT implements a fallback function, the call will still succeed.
}
return 0x00;
} catch (bytes memory err) {
return _handleError(err);
}
}
//--- Underwriting ---//
// The following section contains the underwriting module of Catalyst.
// It serves to speedup swap execution by letting a thirdparty take on the confirmation / delivery risk
// by pre-executing the latter part and then reserving the swap result in the escrow.
/**
* @notice Computes the underwriting identifier for a Catalyst swap.
*/
function _getUnderwriteIdentifier(
address targetVault,
address toAsset,
uint256 U,
uint256 minOut,
address toAccount,
uint16 underwriteIncentiveX16,
bytes calldata cdata
) internal pure returns (bytes32 identifier) {
return identifier = keccak256(
bytes.concat(
bytes20(targetVault),
bytes20(toAsset),
bytes32(U),
bytes32(minOut),
bytes20(toAccount),
bytes2(underwriteIncentiveX16),
cdata
)
);
}
/**
* @notice Computes the underwriting identifier for a Catalyst swap.
*/
function getUnderwriteIdentifier(
address targetVault,
address toAsset,
uint256 U,
uint256 minOut,
address toAccount,
uint16 underwriteIncentiveX16,
bytes calldata cdata
) override external pure returns (bytes32 identifier) {
return identifier = _getUnderwriteIdentifier(
targetVault,
toAsset,
U,
minOut,
toAccount,
underwriteIncentiveX16,
cdata
);
}
/**
* @notice Underwrites a swap and check if there is a connection.
* @dev sourceIdentifier and fromVault are not verified that they belong to the message.
* As a result, care should be placed on correctly reading these.
*/
function underwriteAndCheckConnection(
bytes32 sourceIdentifier,
bytes calldata fromVault, // -- Conection Check
address targetVault, // -- Swap information
address toAsset,
uint256 U,
uint256 minOut,
address toAccount,
uint16 underwriteIncentiveX16,
bytes calldata cdata
) override external {
if (!ICatalystV1Vault(targetVault)._vaultConnection(sourceIdentifier, fromVault)) revert NoVaultConnection();
underwrite(
targetVault,
toAsset,
U,
minOut,
toAccount,
underwriteIncentiveX16,
cdata
);
}
/**
* @notice Underwrite a swap.
* There are a few important properties that underwriters should be aware of:
* 1. There is nothing that checks if the connection is valid on this call. This check should be done seperately.
* Even if a swap is executed where it appears that it is possible to execute the swap,
* these values should be read and a connection should be checked. (via this function).
* As you can underwrite the swap (where there is no connection) but when the swap arrives it
* does not fill the underwrite but instead fails on ack and releases the input on the source chain.
* You can use the similar function underwriteAndCheckConnection to also check the connection.
*
* 2. You are underwriting the specific instance of the transaction NOT the inclusion of the transaction.
* What this means for you, is that if the block of the transaction is lost/abandon/re-entered, then
* the underwriting will not be noted unless the transaction is re-executed EXACTLY as it was before.
* The most important parameter is U which is volatile and any change to the vault balances on the source chain
* will cause U to be different.
* In other words, if that transaction is re-executed before or after another swap and it wasn't the case before,
* then it won't fill the underwrite anymore and either be exeucted as an ordinary swap (to the user) or fail
* on ack and release the original funds back to the user.
*
* 3. The execution of the underwrite is dependent on the correct execution of both
* the minout but also the additional logic. If either fails, then the swap is not
* underwritable. To avoid wasting gas, it is important that the underwrite is simulated before executed.
*/
function underwrite(
address targetVault, // -- Swap information
address toAsset,
uint256 U,
uint256 minOut,
address toAccount,
uint16 underwriteIncentiveX16,
bytes calldata cdata
) public returns(bytes32 identifier) {
// Get the swap identifier.
// For any incoming swap, the swap will have a semi-unique identifier, which can be derived solely based on the
// swap parameters. Assuming that the swap has already been executed, these arguments are constant and known.
// As a result, once the swap arrives (and the underwriter is competent), the same identifier will be computed and matched.
// For other implementations: The arguments the identifier is based on should be the same but the hashing
// algorithm doesn't matter. It is only used and computed on the destination chain.
identifier = _getUnderwriteIdentifier(
targetVault,
toAsset,
U,
minOut,
toAccount,
underwriteIncentiveX16,
cdata
);
// Observation: A swapper can execute multiple swaps which return the same U.
// In these cases, it would not be possible to underwrite the second swap (or further swaps)
// until after the first swap has arrived. This can be counteracted by either:
// 1. Changing U. The tail of U is **volatile**. As a result, to get overlapping identifiers,
// it would have to be deliberatly.
// 2. Add random noise to minOut or underwriteIncentiveX16. For tokens with 18 decimals, this noise can
// be as small as 1e-12 then the chance that 2 swaps collide would be 1 in a million. (literally 1/(1e(18-12)) = 1/1e6)
// 3. Add either a counter or noise to cdata.
// For most implementations, the observation can be ignored because of the strength of point 1.
// Check if the associated underwrite just arrived and has already been matched.
// This is an issue when the swap was JUST underwriten, JUST arrived (and matched), AND someone else JUST underwrote the swap.
// To give underwriters a bit more protection, we add a buffer of size `BUFFER_BLOCKS`.
// Error SwapAlreadyUnderwritten vs Error SwapRecentlyUnderwritten: It is very likely that this block is triggered not because a swap was
// fulfilled but because it has already been underwritten.
// That is because (lastTouchBlock + BUFFER_BLOCKS >= uint96(block.number)) WILL ALWAYS be true when a swap has been underwritten, thus
// SwapRecentlyUnderwritten will be the error. You might have expected the error "SwapAlreadyUnderwritten". However, we never get there so
// it cannot emit. We also cannot move that check up here, since then an external call would be made between a state check and a
// state modification (reentry). As a result, SwapRecentlyUnderwritten will be emitted when a swap has already been underwritten EXCEPT
// when underwriting a swap through reentry. Then the reentry will underwrite the swap and the main call will fail with SwapAlreadyUnderwritten.
UnderwritingStorage storage underwriteState = underwritingStorage[identifier];
unchecked {
// Get the last touch block. For most underwrites it is going to be 0.
uint96 lastTouchBlock = underwriteState.expiry;
if (lastTouchBlock != 0) { // implies that the swap has never been underwritten.
// if lastTouchBlock > type(uint96).max + BUFFER_BLOCKS then lastTouchBlock + BUFFER_BLOCKS overflows.
// if lastTouchBlock < BUFFER_BLOCKS then lastTouchBlock - BUFFER_BLOCKS underflows.
if ((lastTouchBlock < type(uint96).max - BUFFER_BLOCKS) && (lastTouchBlock > BUFFER_BLOCKS)) {
// Add a reasonable buffer so if the transaction got into the memory pool and is delayed into the next blocks
// it doesn't underwrite a non-existing swap.
if (lastTouchBlock + BUFFER_BLOCKS >= uint96(block.number)) {
// Check that uint96(block.number) hasn't overflowed and this is an old reference. We don't care about the underflow
// as that will always return false.
if (lastTouchBlock - BUFFER_BLOCKS <= uint96(block.number)) revert SwapRecentlyUnderwritten();
}
} else {
if (lastTouchBlock == uint96(block.number)) revert SwapRecentlyUnderwritten();
}
}
}
// Get the number of purchased units from the vault.
// This uses a custom call that doesn't return any assets. The cal escrows purchasedTokens on the vault.
// Importantly! The connection is not checked here. Instead it is checked when the
// message arrives. As a result, the underwriter should verify that a message is good.
uint256 purchasedTokens = ICatalystV1Vault(targetVault).underwriteAsset(
identifier,
toAsset,
U,
minOut * (1 << 16) / ((1 << 16) - uint256(underwriteIncentiveX16)) // minout is checked after underwrite fee.
);
// The following number of lines act as re-entry protection. Do not add any external call inbetween these lines.
// Ensure the swap hasn't already been underwritten by checking if refundTo is set.
// This line is very unlikely to ever get emitted. Instead, read the comment about SwapRecentlyUnderwritten.
if (underwriteState.refundTo != address(0)) revert SwapAlreadyUnderwritten();
uint96 underwriteExpiry = uint96(uint256(block.number) + uint256(maxUnderwritingDuration)); // Should never overflow.
// Save the underwriting state.
underwriteState.tokens = purchasedTokens;
underwriteState.refundTo = msg.sender;
underwriteState.expiry = underwriteExpiry;
// The above combination of lines act as local re-entry protection. Do not add any external call inbetween these lines.
// Collect tokens and collateral from underwriter.
// We still collect the tokens used to incentivise the underwriter as otherwise they could freely reserve liquidity
// in the vaults. Vaults would essentially be a free source of short term options which isn't wanted.
SafeTransferLib.safeTransferFrom(
toAsset,
msg.sender,
address(this),
purchasedTokens * (
UNDERWRITING_COLLATERAL_DENOMINATOR+UNDERWRITING_COLLATERAL
)/UNDERWRITING_COLLATERAL_DENOMINATOR
);
uint256 underwritingIncentive = (purchasedTokens * uint256(underwriteIncentiveX16)) >> 16;
// Subtract the underwrite incentive from the funds sent to the user.
unchecked {
// underwritingIncentive <= purchasedTokens.
purchasedTokens -= underwritingIncentive;
}
// Send the assets to the user.
SafeTransferLib.safeTransfer(toAsset,toAccount, purchasedTokens);
// Figure out if the user wants to execute additional logic.
// Note that this logic is not contained within a try catch. It could fail.
// An underwrite should simulate the tx execution before submitting the transaction as otherwise
// they could be out the associated gas.
uint16 calldataLength = uint16(bytes2(cdata[0:2]));
if (calldataLength != 0) {
address dataTarget = address(bytes20(cdata[2:2+20]));
bytes calldata customCalldata = cdata[2+20:2+calldataLength];
ICatalystReceiver(dataTarget).onCatalystCall(purchasedTokens, customCalldata, true);
}
emit SwapUnderwritten(
identifier,
msg.sender,
underwriteExpiry,
targetVault,
toAsset,
U,
toAccount,
purchasedTokens
);
}
/**
* @notice Resolves unexpired underwrites so that the escrowed value can be freed.
* @dev The underwrite owner can expire it at any time. Other callers needs to wait until after expiry.
*/
function expireUnderwrite(
address targetVault,
address toAsset,
uint256 U,
uint256 minOut,
address toAccount,
uint16 underwriteIncentiveX16,
bytes calldata cdata
) override external {
bytes32 identifier = _getUnderwriteIdentifier(
targetVault,
toAsset,
U,
minOut,
toAccount,
underwriteIncentiveX16,
cdata
);
UnderwritingStorage storage underwriteState = underwritingStorage[identifier];
// Check that the refundTo address is set. (Indicates that the underwrite exists.)
address refundAddress = underwriteState.refundTo;
if (refundAddress == address(0)) revert UnderwriteDoesNotExist(identifier);
// Check that the underwriting can be expired. If the msg.sender is the refundTo address, then it can be expired at any time.
// This lets the underwriter reclaim _some_ of the collateral they provided if they change their mind or observed an issue.
// Load the associated storage slot.
uint256 underWrittenTokens = underwriteState.tokens;
uint256 expiryTime = uint256(underwriteState.expiry);
if (msg.sender != refundAddress) {
// Otherwise, the expiry time must have been passed.
if (underwriteState.expiry > block.number) revert UnderwriteNotExpired(expiryTime - block.number);
}
// The next line acts as reentry protection. When the storage is deleted refundAddress == address(0) will be true.
delete underwritingStorage[identifier];
// Delete the escrow
ICatalystV1Vault(targetVault).deleteUnderwriteAsset(identifier, U, underWrittenTokens, toAsset);
unchecked {
// Compute the underwriting incentive.
// Notice the parts that we only have: incentive + collateral to work with
// The incentive was never sent to the user, neither was the underwriting incentive.
uint256 underwritingIncentive = (underWrittenTokens * uint256(underwriteIncentiveX16)) >> 16;
// This computation has been done before.
// Get the collateral.
// A larger computation has already been done when the swap was initially underwritten.
uint256 refundAmount = underWrittenTokens * (
UNDERWRITING_COLLATERAL
)/UNDERWRITING_COLLATERAL_DENOMINATOR + underwritingIncentive;
// collateral + underwritingIncentive must be less than the full amount.
// Send the coded shares of the collateral to the expirer the rest to the vault.
// This following logic might overflow but we would rather have it overflow (which reduces expireShare)
// than to never be able to expire an underwrite.
uint256 expireShare = refundAmount * EXPIRE_CALLER_REWARD / EXPIRE_CALLER_REWARD_DENOMINATOR;
SafeTransferLib.safeTransfer(toAsset, msg.sender, expireShare);
// refundAmount > expireShare, and specially when expireShare overflows.
uint256 vaultShare = refundAmount - expireShare;
SafeTransferLib.safeTransfer(toAsset, targetVault, vaultShare);
emit ExpireUnderwrite(
identifier,
msg.sender,
expireShare
);
}
// The underwriting storage has already been deleted.
}
/** @dev It is important that any call to this functions has pre-checked the vault connection. */
function _matchUnderwrite(
bytes32 identifier,
address toAsset,
address vault,
bytes32 sourceIdentifier,
bytes calldata fromVault,
uint16 underwriteIncentiveX16
) internal returns (bool swapUnderwritten) {
UnderwritingStorage storage underwriteState = underwritingStorage[identifier];
// Load number of tokens from storage.
uint256 underwrittenTokenAmount = underwriteState.tokens;
// Check if the swap was underwritten => refundTo != address(0)
address refundTo = underwriteState.refundTo;
// if refundTo == address(0) then the swap hasn't been underwritten.
if (refundTo == address(0)) return swapUnderwritten = false;
// Reentry protection. No external calls are allowed before this line. The line 'if (refundTo == address(0)) ...' will always be true.
delete underwritingStorage[identifier];
// Set the last touch block so someone doesn't underwrite this swap again.
underwritingStorage[identifier].expiry = uint96(block.number);
// Delete escrow information and send swap tokens directly to the underwriter.
ICatalystV1Vault(vault).releaseUnderwriteAsset(refundTo, identifier, underwrittenTokenAmount, toAsset, sourceIdentifier, fromVault);
// We now only need to handle the collateral and underwriting incentive.
// We don't have to check that the vault didn't lie to us about underwriting.
// Also refund the collateral.
uint256 refundAmount = underwrittenTokenAmount * (
UNDERWRITING_COLLATERAL
)/UNDERWRITING_COLLATERAL_DENOMINATOR;
// add the underwriting incentive as well. Notice that 2x refundAmount are in play.
// 1. The first part comes from the underwriter + collateral.
// + 1. The second part comes from the vault after the matching message arrives.
// = 2 parts
// 1 - incentive has been sent to the user.
// That leaves us with part (1 - (1 - incentive)) + (1) = incentive + 1
uint256 underwritingIncentive = (underwrittenTokenAmount * uint256(underwriteIncentiveX16)) >> 16;
refundAmount += underwritingIncentive;
SafeTransferLib.safeTransfer(toAsset, refundTo, refundAmount);
emit FulfillUnderwrite(
identifier
);
return swapUnderwritten = true;
}
function _handleReceiveAsset(bytes32 sourceIdentifier, bytes calldata data) internal returns (bytes1 acknowledgement) {
// We don't know how from_vault is encoded. So we load it as bytes. Including the length.
bytes calldata fromVault = data[ FROM_VAULT_LENGTH_POS : FROM_VAULT_END ];
// We know that toVault is an EVM address
address toVault = address(bytes20(data[ TO_VAULT_START_EVM : TO_VAULT_END ]));
// Select excess calldata. Excess calldata is not decoded.
bytes calldata cdata = data[CTX0_DATA_LENGTH_START:];
// Get the toAsset
uint8 toAssetIndex = uint8(data[CTX0_TO_ASSET_INDEX_POS]);
address toAsset = ICatalystV1Vault(toVault)._tokenIndexing(toAssetIndex);
// Get the rest of the swap parameters.
address toAccount = address(bytes20(data[ TO_ACCOUNT_START_EVM : TO_ACCOUNT_END ]));
uint256 U = uint256(bytes32(data[ UNITS_START : UNITS_END ]));
uint256 minOut = uint256(bytes32(data[ CTX0_MIN_OUT_START : CTX0_MIN_OUT_END ]));
uint16 underwriteIncentiveX16 = uint16(bytes2(data[CTX0_UW_INCENTIVE_START:CTX0_UW_INCENTIVE_END]));
// Get the underwriting identifier.
bytes32 identifier = _getUnderwriteIdentifier(
toVault,
toAsset,
U,
minOut,
toAccount,
underwriteIncentiveX16,
cdata
);
// Check if the swap has been underwritten. If it has, return funds to underwriter.
bool swapUnderwritten = _matchUnderwrite(
identifier,
toAsset,
toVault,
sourceIdentifier,
fromVault,
underwriteIncentiveX16
);
if (!swapUnderwritten) {
// The swap hasn't been underwritten lets execute the swap properly.
return acknowledgement = _handleReceiveAssetFallback(sourceIdentifier, data);
}
// There is no case where only a subset of the units are filled. As either the complete swap (through the swap identifier)
// is underwritten or it wasn't underwritten.
return acknowledgement = 0x00;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple ERC20 + EIP-2612 implementation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)
///
/// @dev Note:
/// - The ERC20 standard allows minting and transferring to and from the zero address,
/// minting and transferring zero tokens, as well as self-approvals.
/// For performance, this implementation WILL NOT revert for such actions.
/// Please add any checks with overrides if desired.
/// - The `permit` function uses the ecrecover precompile (0x1).
///
/// If you are overriding:
/// - NEVER violate the ERC20 invariant:
/// the total sum of all balances must be equal to `totalSupply()`.
/// - Check that the overridden function is actually used in the function you want to
/// change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC20 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The total supply has overflowed.
error TotalSupplyOverflow();
/// @dev The allowance has overflowed.
error AllowanceOverflow();
/// @dev The allowance has underflowed.
error AllowanceUnderflow();
/// @dev Insufficient balance.
error InsufficientBalance();
/// @dev Insufficient allowance.
error InsufficientAllowance();
/// @dev The permit is invalid.
error InvalidPermit();
/// @dev The permit has expired.
error PermitExpired();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
event Transfer(address indexed from, address indexed to, uint256 amount);
/// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
event Approval(address indexed owner, address indexed spender, uint256 amount);
/// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
uint256 private constant _TRANSFER_EVENT_SIGNATURE =
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
/// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
uint256 private constant _APPROVAL_EVENT_SIGNATURE =
0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The storage slot for the total supply.
uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c;
/// @dev The balance slot of `owner` is given by:
/// ```
/// mstore(0x0c, _BALANCE_SLOT_SEED)
/// mstore(0x00, owner)
/// let balanceSlot := keccak256(0x0c, 0x20)
/// ```
uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2;
/// @dev The allowance slot of (`owner`, `spender`) is given by:
/// ```
/// mstore(0x20, spender)
/// mstore(0x0c, _ALLOWANCE_SLOT_SEED)
/// mstore(0x00, owner)
/// let allowanceSlot := keccak256(0x0c, 0x34)
/// ```
uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20;
/// @dev The nonce slot of `owner` is given by:
/// ```
/// mstore(0x0c, _NONCES_SLOT_SEED)
/// mstore(0x00, owner)
/// let nonceSlot := keccak256(0x0c, 0x20)
/// ```
uint256 private constant _NONCES_SLOT_SEED = 0x38377508;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`.
uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901;
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
bytes32 private constant _DOMAIN_TYPEHASH =
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
/// @dev `keccak256("1")`.
bytes32 private constant _VERSION_HASH =
0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;
/// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`.
bytes32 private constant _PERMIT_TYPEHASH =
0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 METADATA */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the name of the token.
function name() public view virtual returns (string memory);
/// @dev Returns the symbol of the token.
function symbol() public view virtual returns (string memory);
/// @dev Returns the decimals places of the token.
function decimals() public view virtual returns (uint8) {
return 18;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the amount of tokens in existence.
function totalSupply() public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_TOTAL_SUPPLY_SLOT)
}
}
/// @dev Returns the amount of tokens owned by `owner`.
function balanceOf(address owner) public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
function allowance(address owner, address spender)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x34))
}
}
/// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
///
/// Emits a {Approval} event.
function approve(address spender, uint256 amount) public virtual returns (bool) {
/// @solidity memory-safe-assembly
assembly {
// Compute the allowance slot and store the amount.
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x34), amount)
// Emit the {Approval} event.
mstore(0x00, amount)
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
}
return true;
}
/// @dev Transfer `amount` tokens from the caller to `to`.
///
/// Requirements:
/// - `from` must at least have `amount`.
///
/// Emits a {Transfer} event.
function transfer(address to, uint256 amount) public virtual returns (bool) {
_beforeTokenTransfer(msg.sender, to, amount);
/// @solidity memory-safe-assembly
assembly {
// Compute the balance slot and load its value.
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, caller())
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Compute the balance slot of `to`.
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance of `to`.
// Will not overflow because the sum of all user balances
// cannot exceed the maximum uint256 value.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c)))
}
_afterTokenTransfer(msg.sender, to, amount);
return true;
}
/// @dev Transfers `amount` tokens from `from` to `to`.
///
/// Note: Does not update the allowance if it is the maximum uint256 value.
///
/// Requirements:
/// - `from` must at least have `amount`.
/// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
///
/// Emits a {Transfer} event.
function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
_beforeTokenTransfer(from, to, amount);
/// @solidity memory-safe-assembly
assembly {
let from_ := shl(96, from)
// Compute the allowance slot and load its value.
mstore(0x20, caller())
mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
let allowanceSlot := keccak256(0x0c, 0x34)
let allowance_ := sload(allowanceSlot)
// If the allowance is not the maximum uint256 value.
if add(allowance_, 1) {
// Revert if the amount to be transferred exceeds the allowance.
if gt(amount, allowance_) {
mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated allowance.
sstore(allowanceSlot, sub(allowance_, amount))
}
// Compute the balance slot and load its value.
mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Compute the balance slot of `to`.
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance of `to`.
// Will not overflow because the sum of all user balances
// cannot exceed the maximum uint256 value.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
}
_afterTokenTransfer(from, to, amount);
return true;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EIP-2612 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev For more performance, override to return the constant value
/// of `keccak256(bytes(name()))` if `name()` will never change.
function _constantNameHash() internal view virtual returns (bytes32 result) {}
/// @dev Returns the current nonce for `owner`.
/// This value is used to compute the signature for EIP-2612 permit.
function nonces(address owner) public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
// Compute the nonce slot and load its value.
mstore(0x0c, _NONCES_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`,
/// authorized by a signed approval by `owner`.
///
/// Emits a {Approval} event.
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
bytes32 nameHash = _constantNameHash();
// We simply calculate it on-the-fly to allow for cases where the `name` may change.
if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
/// @solidity memory-safe-assembly
assembly {
// Revert if the block timestamp is greater than `deadline`.
if gt(timestamp(), deadline) {
mstore(0x00, 0x1a15a3cc) // `PermitExpired()`.
revert(0x1c, 0x04)
}
let m := mload(0x40) // Grab the free memory pointer.
// Clean the upper 96 bits.
owner := shr(96, shl(96, owner))
spender := shr(96, shl(96, spender))
// Compute the nonce slot and load its value.
mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX)
mstore(0x00, owner)
let nonceSlot := keccak256(0x0c, 0x20)
let nonceValue := sload(nonceSlot)
// Prepare the domain separator.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), _VERSION_HASH)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
mstore(0x2e, keccak256(m, 0xa0))
// Prepare the struct hash.
mstore(m, _PERMIT_TYPEHASH)
mstore(add(m, 0x20), owner)
mstore(add(m, 0x40), spender)
mstore(add(m, 0x60), value)
mstore(add(m, 0x80), nonceValue)
mstore(add(m, 0xa0), deadline)
mstore(0x4e, keccak256(m, 0xc0))
// Prepare the ecrecover calldata.
mstore(0x00, keccak256(0x2c, 0x42))
mstore(0x20, and(0xff, v))
mstore(0x40, r)
mstore(0x60, s)
let t := staticcall(gas(), 1, 0, 0x80, 0x20, 0x20)
// If the ecrecover fails, the returndatasize will be 0x00,
// `owner` will be checked if it equals the hash at 0x00,
// which evaluates to false (i.e. 0), and we will revert.
// If the ecrecover succeeds, the returndatasize will be 0x20,
// `owner` will be compared against the returned address at 0x20.
if iszero(eq(mload(returndatasize()), owner)) {
mstore(0x00, 0xddafbaef) // `InvalidPermit()`.
revert(0x1c, 0x04)
}
// Increment and store the updated nonce.
sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds.
// Compute the allowance slot and store the value.
// The `owner` is already at slot 0x20.
mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender))
sstore(keccak256(0x2c, 0x34), value)
// Emit the {Approval} event.
log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/// @dev Returns the EIP-712 domain separator for the EIP-2612 permit.
function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) {
bytes32 nameHash = _constantNameHash();
// We simply calculate it on-the-fly to allow for cases where the `name` may change.
if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Grab the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), _VERSION_HASH)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
result := keccak256(m, 0xa0)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL MINT FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Mints `amount` tokens to `to`, increasing the total supply.
///
/// Emits a {Transfer} event.
function _mint(address to, uint256 amount) internal virtual {
_beforeTokenTransfer(address(0), to, amount);
/// @solidity memory-safe-assembly
assembly {
let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT)
let totalSupplyAfter := add(totalSupplyBefore, amount)
// Revert if the total supply overflows.
if lt(totalSupplyAfter, totalSupplyBefore) {
mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`.
revert(0x1c, 0x04)
}
// Store the updated total supply.
sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter)
// Compute the balance slot and load its value.
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c)))
}
_afterTokenTransfer(address(0), to, amount);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL BURN FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Burns `amount` tokens from `from`, reducing the total supply.
///
/// Emits a {Transfer} event.
function _burn(address from, uint256 amount) internal virtual {
_beforeTokenTransfer(from, address(0), amount);
/// @solidity memory-safe-assembly
assembly {
// Compute the balance slot and load its value.
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, from)
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Subtract and store the updated total supply.
sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount))
// Emit the {Transfer} event.
mstore(0x00, amount)
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
}
_afterTokenTransfer(from, address(0), amount);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL TRANSFER FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Moves `amount` of tokens from `from` to `to`.
function _transfer(address from, address to, uint256 amount) internal virtual {
_beforeTokenTransfer(from, to, amount);
/// @solidity memory-safe-assembly
assembly {
let from_ := shl(96, from)
// Compute the balance slot and load its value.
mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Compute the balance slot of `to`.
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance of `to`.
// Will not overflow because the sum of all user balances
// cannot exceed the maximum uint256 value.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
}
_afterTokenTransfer(from, to, amount);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL ALLOWANCE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Updates the allowance of `owner` for `spender` based on spent `amount`.
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute the allowance slot and load its value.
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, owner)
let allowanceSlot := keccak256(0x0c, 0x34)
let allowance_ := sload(allowanceSlot)
// If the allowance is not the maximum uint256 value.
if add(allowance_, 1) {
// Revert if the amount to be transferred exceeds the allowance.
if gt(amount, allowance_) {
mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated allowance.
sstore(allowanceSlot, sub(allowance_, amount))
}
}
}
/// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
///
/// Emits a {Approval} event.
function _approve(address owner, address spender, uint256 amount) internal virtual {
/// @solidity memory-safe-assembly
assembly {
let owner_ := shl(96, owner)
// Compute the allowance slot and store the amount.
mstore(0x20, spender)
mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED))
sstore(keccak256(0x0c, 0x34), amount)
// Emit the {Approval} event.
mstore(0x00, amount)
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c)))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HOOKS TO OVERRIDE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Hook that is called before any transfer of tokens.
/// This includes minting and burning.
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/// @dev Hook that is called after any transfer of tokens.
/// This includes minting and burning.
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - For ERC20s, this implementation won't check that a token has code,
/// responsibility is delegated to the caller.
library SafeTransferLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ETH transfer has failed.
error ETHTransferFailed();
/// @dev The ERC20 `transferFrom` has failed.
error TransferFromFailed();
/// @dev The ERC20 `transfer` has failed.
error TransferFailed();
/// @dev The ERC20 `approve` has failed.
error ApproveFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;
/// @dev Suggested gas stipend for contract receiving ETH to perform a few
/// storage reads and writes, but low enough to prevent griefing.
uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ETH OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
//
// The regular variants:
// - Forwards all remaining gas to the target.
// - Reverts if the target reverts.
// - Reverts if the current contract has insufficient balance.
//
// The force variants:
// - Forwards with an optional gas stipend
// (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
// - If the target reverts, or if the gas stipend is exhausted,
// creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
// Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
// - Reverts if the current contract has insufficient balance.
//
// The try variants:
// - Forwards with a mandatory gas stipend.
// - Instead of reverting, returns whether the transfer succeeded.
/// @dev Sends `amount` (in wei) ETH to `to`.
function safeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Sends all the ETH in the current contract to `to`.
function safeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// Transfer all the ETH and check if it succeeded or not.
if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// forgefmt: disable-next-item
if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
}
}
/// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
function trySafeTransferAllETH(address to, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends all of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have their entire balance approved for
/// the current contract to manage.
function safeTransferAllFrom(address token, address from, address to)
internal
returns (uint256 amount)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransfer(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sends all of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransferAll(address token, address to) internal returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
mstore(0x20, address()) // Store the address of the current contract.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x14, to) // Store the `to` argument.
amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// Reverts upon failure.
function safeApprove(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
/// then retries the approval again (some tokens, e.g. USDT, requires this).
/// Reverts upon failure.
function safeApproveWithRetry(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, retrying upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x34, 0) // Store 0 for the `amount`.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
mstore(0x34, amount) // Store back the original `amount`.
// Retry the approval, reverting upon failure.
if iszero(
and(
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Returns the amount of ERC20 `token` owned by `account`.
/// Returns zero if the `token` does not exist.
function balanceOf(address token, address account) internal view returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, account) // Store the `account` argument.
mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
amount :=
mul(
mload(0x20),
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The caller is not authorized to call the function.
error Unauthorized();
/// @dev The `newOwner` cannot be the zero address.
error NewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.
error NoHandoverRequest();
/// @dev Cannot double-initialize.
error AlreadyInitialized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ownership is transferred from `oldOwner` to `newOwner`.
/// This event is intentionally kept the same as OpenZeppelin's Ownable to be
/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
/// despite it not being as lightweight as a single argument event.
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.
event OwnershipHandoverRequested(address indexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.
event OwnershipHandoverCanceled(address indexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The owner slot is given by:
/// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
/// It is intentionally chosen to be a high value
/// to avoid collision with lower slots.
/// The choice of manual storage layout is to enable compatibility
/// with both regular and upgradeable contracts.
bytes32 internal constant _OWNER_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
/// The ownership handover slot of `newOwner` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
/// let handoverSlot := keccak256(0x00, 0x20)
/// ```
/// It stores the expiry timestamp of the two-step ownership handover.
uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
/// @dev Initializes the owner directly without authorization guard.
/// This function must be called upon initialization,
/// regardless of whether the contract is upgradeable or not.
/// This is to enable generalization to both regular and upgradeable contracts,
/// and to save gas in case the initial owner is not the caller.
/// For performance reasons, this function will not check if there
/// is an existing owner.
function _initializeOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
if sload(ownerSlot) {
mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
revert(0x1c, 0x04)
}
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
} else {
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(_OWNER_SLOT, newOwner)
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
}
/// @dev Sets the owner directly without authorization guard.
function _setOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
}
} else {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, newOwner)
}
}
}
/// @dev Throws if the sender is not the owner.
function _checkOwner() internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner, revert.
if iszero(eq(caller(), sload(_OWNER_SLOT))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds.
/// Override to return a different value if needed.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
return 48 * 3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to transfer the ownership to `newOwner`.
function transferOwnership(address newOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
if iszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Allows the owner to renounce their ownership.
function renounceOwnership() public payable virtual onlyOwner {
_setOwner(address(0));
}
/// @dev Request a two-step ownership handover to the caller.
/// The request will automatically expire in 48 hours (172800 seconds) by default.
function requestOwnershipHandover() public payable virtual {
unchecked {
uint256 expires = block.timestamp + _ownershipHandoverValidFor();
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to `expires`.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
// Emit the {OwnershipHandoverRequested} event.
log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
/// @dev Cancels the two-step ownership handover to the caller, if any.
function cancelOwnershipHandover() public payable virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
// Emit the {OwnershipHandoverCanceled} event.
log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
/// Reverts if there is no existing ownership handover requested by `pendingOwner`.
function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot := keccak256(0x0c, 0x20)
// If the handover does not exist, or has expired.
if gt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
revert(0x1c, 0x04)
}
// Set the handover slot to 0.
sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the owner of the contract.
function owner() public view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_OWNER_SLOT)
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
function ownershipHandoverExpiresAt(address pendingOwner)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
// Compute the handover slot.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result := sload(keccak256(0x0c, 0x20))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by the owner.
modifier onlyOwner() virtual {
_checkOwner();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMessageEscrowStructs {
struct IncentiveDescription {
uint48 maxGasDelivery; // 0: 6/32 bytes
uint48 maxGasAck; // 0: 12/32 bytes
address refundGasTo; // 0: 32/32 bytes
uint96 priceOfDeliveryGas; // 1: 12/32 bytes
uint96 priceOfAckGas; // 1: 24/32 bytes
uint64 targetDelta; // 1: 32/32 bytes
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IMessageEscrowStructs } from "./IMessageEscrowStructs.sol";
import { IMessageEscrowErrors } from "./IMessageEscrowErrors.sol";
import { IMessageEscrowEvents } from "./IMessageEscrowEvents.sol";
interface IIncentivizedMessageEscrow is IMessageEscrowStructs, IMessageEscrowErrors, IMessageEscrowEvents {
function bounty(address fromApplication, bytes32 destinationIdentifier, bytes32 messageIdentifier) external view returns(IncentiveDescription memory incentive);
function messageDelivered(bytes32 sourceIdentifier, bytes calldata sourceImplementationIdentifier, bytes32 messageIdentifier) external view returns(bytes32 hasMessageBeenExecuted);
function increaseBounty(
address fromApplication,
bytes32 destinationIdentifier,
bytes32 messageIdentifier,
uint96 priceOfDeliveryGas,
uint96 priceOfAckGas
) external payable;
function submitMessage(
bytes32 destinationIdentifier,
bytes calldata destinationAddress,
bytes calldata message,
IncentiveDescription calldata incentive,
uint64 deadline
) external payable returns(uint256 gasRefund, bytes32 messageIdentifier);
function processPacket(bytes calldata messagingProtocolContext, bytes calldata message, bytes32 feeRecipient) payable external;
function setRemoteImplementation(bytes32 chainIdentifier, bytes calldata implementation) external;
/**
* @notice Estimates the additional cost to the messaging router to validate the message
* @return asset The asset the token is in. If native token, returns address(0);
* @return amount The number of assets to pay.
*/
function estimateAdditionalCost() external view returns(address asset, uint256 amount);
function timeoutMessage(
bytes32 sourceIdentifier,
bytes calldata implementationIdentifier,
uint256 originBlockNumber,
bytes calldata message
) external payable;
function reemitAckMessage(
bytes32 sourceIdentifier,
bytes calldata implementationIdentifier,
bytes calldata receiveAckWithContext
) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
contract Bytes65 {
error InvalidBytes65Address();
function _checkBytes65(bytes calldata supposedlyBytes65) internal pure returns(bool) {
return supposedlyBytes65.length == 65;
}
modifier checkBytes65Address(bytes calldata supposedlyBytes65) {
if (!_checkBytes65(supposedlyBytes65)) revert InvalidBytes65Address();
_;
}
function convertEVMTo65(address evmAddress) public pure returns(bytes memory) {
return bytes.concat(
bytes1(uint8(20)), // Size of address. Is always 20 for EVM
bytes32(0), // First 32 bytes on EVM are 0
bytes32(uint256(uint160((evmAddress)))) // Encode the address in bytes32.
);
}
function thisBytes65() public view returns(bytes memory) {
return convertEVMTo65(address(this));
}
}//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { ICatalystV1Structs } from "./ICatalystV1VaultState.sol";
import { ICrossChainReceiver } from "GeneralisedIncentives/src/interfaces/ICrossChainReceiver.sol";
// Autogenerated
interface ICatalystChainInterface is ICatalystV1Structs, ICrossChainReceiver {
function EXPIRE_CALLER_REWARD() external view returns (uint256);
function EXPIRE_CALLER_REWARD_DENOMINATOR() external view returns (uint256);
function UNDERWRITING_COLLATERAL() external view returns (uint256);
function UNDERWRITING_COLLATERAL_DENOMINATOR() external view returns (uint256);
function chainIdentifierToDestinationAddress(bytes32) external view returns (bytes memory);
function connectNewChain(bytes32 chainIdentifier, bytes memory remoteCCI, bytes memory remoteGARP) external;
function estimateAdditionalCost() external view returns (address asset, uint256 amount);
function expireUnderwrite(
address targetVault,
address toAsset,
uint256 U,
uint256 minOut,
address toAccount,
uint16 underwriteIncentiveX16,
bytes memory cdata
) external;
function getUnderwriteIdentifier(
address targetVault,
address toAsset,
uint256 U,
uint256 minOut,
address toAccount,
uint16 underwriteIncentiveX16,
bytes memory cdata
) external pure returns (bytes32 identifier);
function maxUnderwritingDuration() external view returns (uint256);
function minGasFor(bytes32) external view returns (uint48);
function sendCrossChainAsset(
RouteDescription memory routeDescription,
uint8 toAssetIndex,
uint256 U,
uint256 minOut,
uint256 fromAmount,
address fromAsset,
uint16 underwriteIncentiveX16,
bytes memory calldata_
) external payable;
function sendCrossChainLiquidity(
RouteDescription memory routeDescription,
uint256 U,
uint256[2] memory minOut,
uint256 fromAmount,
bytes memory calldata_
) external payable;
function setMaxUnderwritingDuration(uint256 newMaxUnderwriteDuration) external;
function setMinGasFor(bytes32 chainIdentifier, uint48 minGas) external;
function underwrite(
address targetVault,
address toAsset,
uint256 U,
uint256 minOut,
address toAccount,
uint16 underwriteIncentiveX16,
bytes memory cdata
) external returns (bytes32 identifier);
function underwriteAndCheckConnection(
bytes32 sourceIdentifier,
bytes memory fromVault,
address targetVault,
address toAsset,
uint256 U,
uint256 minOut,
address toAccount,
uint16 underwriteIncentiveX16,
bytes memory cdata
) external;
function underwritingStorage(bytes32) external view returns (uint256 tokens, address refundTo, uint96 expiry);
}//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
interface ICatalystReceiver {
/**
* @notice The callback from a catalyst call. To determine if the swap was an asset or liquidity swap, either the current balance should be checked or it should be encoded into data.
* @dev If you want full finality (not just economical finality)
* revert on underwritten == true.
*/
function onCatalystCall(uint256 purchasedTokens, bytes calldata data, bool underwritten) external;
}//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { IMessageEscrowStructs } from "GeneralisedIncentives/src/interfaces/IMessageEscrowStructs.sol";
interface ICatalystV1Structs is IMessageEscrowStructs {
/**
* @param chainIdentifier target chain identifier.
* @param toVault Target vault on the target chain. Encoded in 64 + 1 bytes.
* @param toAccount Recipient of the transaction on the target chain. Encoded in 64 + 1 bytes.
* @param incentive Cross-chain relaying incentive description.
*/
struct RouteDescription {
bytes32 chainIdentifier;
bytes toVault;
bytes toAccount;
IncentiveDescription incentive;
uint64 deadline;
}
}
/**
* @title Vault state
* @notice Contains all vault storage which depends on the vault state.
*/
interface ICatalystV1VaultState {
/** @notice Token weights. Used for maintaining a non symmetric vault asset balance. */
function _weight(address token) external view returns (uint256);
function _adjustmentTarget() external view returns (uint48);
function _lastModificationTime() external view returns (uint48);
/** @notice The vault fee in WAD. Implementation of fee: mulWadDown(_amount, _vaultFee) */
function _vaultFee() external view returns (uint64);
function _governanceFeeShare() external view returns (uint64);
/** @notice The address of the responsible for adjusting the fees. */
function _feeAdministrator() external view returns (address);
/** @notice The setupMaster is the short-term owner of the vault. They can connect the vault to vaults on other chains. */
function _setupMaster() external view returns (address);
// Security limit
/** @notice The max incoming liquidity flow from the router. */
function _maxUnitCapacity() external view returns (uint256);
// Escrow reference
/** @notice Total current escrowed tokens */
function _escrowedTokens(address token) external view returns (uint256);
/** @notice Find escrow information. Used for both normal swaps and liquidity swaps. */
function _escrowLookup(bytes32 sendAssetHash) external view returns (address);
/** @notice Total current escrowed vault tokens */
function _escrowedVaultTokens() external view returns (uint256);
/** @notice Checks if there is a connection to the described vault */
function _vaultConnection(bytes32 sourceIdentifier, bytes calldata fromVault) external view returns (bool);
function factoryOwner() external view returns (address);
function governanceFeeDestination() external view returns (address);
/**
* @notice External view function purely used to signal if a vault is safe to use.
* @dev Just checks if the setup master has been set to ZERO_ADDRESS. In other words, has finishSetup been called?
*/
function ready() external view returns (bool);
}//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.19;
import "./interfaces/ICatalystV1VaultSuccessFailure.sol";
import "./interfaces/ICatalystV1VaultAdministration.sol";
import "./interfaces/ICatalystV1VaultDerived.sol";
import "./interfaces/ICatalystV1VaultErrors.sol";
import "./interfaces/ICatalystV1VaultEvents.sol";
import "./interfaces/ICatalystV1VaultImmutables.sol";
import "./interfaces/ICatalystV1VaultPermissionless.sol";
import "./interfaces/ICatalystV1VaultState.sol";
import "./interfaces/ICatalystV1Underwriting.sol";
interface ICatalystV1Vault is
ICatalystV1VaultSuccessFailure,
ICatalystV1VaultAdministration,
ICatalystV1VaultDerived,
ICatalystV1VaultEvents,
ICatalystV1VaultImmutables,
ICatalystV1VaultPermissionless,
ICatalystV1VaultState,
ICatalystV1Structs,
ICatalystV1Underwriting
{}//SPDX-License-Identifier: MIT pragma solidity ^0.8.19; // Catalyst Messaging payload structure *********************************************************************************************** // Note: Addresses have 65 bytes reserved, however, the first byte should only be used for the address size. // // Common Payload (beginning) // CONTEXT 0 (1 byte) // + FROM_VAULT_LENGTH 1 (1 byte) // + FROM_VAULT 2 (64 bytes) // + TO_VAULT_LENGTH 66 (1 byte) // + TO_VAULT 67 (64 bytes) // + TO_ACCOUNT_LENGTH 131 (1 byte) // + TO_ACCOUNT 132 (64 bytes) // + UNITS 196 (32 bytes) // // Context-depending Payload // CTX0 - 0x00 - Asset Swap Payload // + TO_ASSET_INDEX 228 (1 byte) // + MIN_OUT 229 (32 bytes) // + FROM_AMOUNT 261 (32 bytes) // + FROM_ASSET_LEN 293 (1 byte) // + FROM_ASSET 294 (64 bytes) // + BLOCK_NUMBER 358 (4 bytes) // (Underwrite Logic) // + UW_INCENTIVE 362 (2 bytes) // // CTX1 - 0x01 - Liquidity Swap Payload // + MIN_OUT 228 (32 bytes) // + MIN_REFERENCE 260 (32 bytes) // + FROM_AMOUNT 292 (32 bytes) // + BLOCK_NUMBER 324 (4 bytes) // // Common Payload (end) // + DATA_LENGTH LENGTH-N-2 (2 bytes) // + DATA LENGTH-N (N bytes) // Contexts ********************************************************************************************************************* bytes1 constant CTX0_ASSET_SWAP = 0x00; bytes1 constant CTX1_LIQUIDITY_SWAP = 0x01; // Common Payload *************************************************************************************************************** uint constant CONTEXT_POS = 0; uint constant FROM_VAULT_LENGTH_POS = 1; uint constant FROM_VAULT_START = 2; uint constant FROM_VAULT_START_EVM = 46; // If the address is an EVM address, this is the start uint constant FROM_VAULT_END = 66; uint constant TO_VAULT_LENGTH_POS = 66; uint constant TO_VAULT_START = 67; uint constant TO_VAULT_START_EVM = 111; // If the address is an EVM address, this is the start uint constant TO_VAULT_END = 131; uint constant TO_ACCOUNT_LENGTH_POS = 131; uint constant TO_ACCOUNT_START = 132; uint constant TO_ACCOUNT_START_EVM = 176; // If the address is an EVM address, this is the start uint constant TO_ACCOUNT_END = 196; uint constant UNITS_START = 196; uint constant UNITS_END = 228; // CTX0 Asset Swap Payload ****************************************************************************************************** uint constant CTX0_TO_ASSET_INDEX_POS = 228; uint constant CTX0_MIN_OUT_START = 229; uint constant CTX0_MIN_OUT_END = 261; uint constant CTX0_FROM_AMOUNT_START = 261; uint constant CTX0_FROM_AMOUNT_END = 293; uint constant CTX0_FROM_ASSET_LENGTH_POS = 293; uint constant CTX0_FROM_ASSET_START = 294; uint constant CTX0_FROM_ASSET_START_EVM = 338; // If the address is an EVM address, this is the start uint constant CTX0_FROM_ASSET_END = 358; uint constant CTX0_BLOCK_NUMBER_START = 358; uint constant CTX0_BLOCK_NUMBER_END = 362; uint constant CTX0_UW_INCENTIVE_START = 362; uint constant CTX0_UW_INCENTIVE_END = 364; uint constant CTX0_DATA_LENGTH_START = 364; uint constant CTX0_DATA_LENGTH_END = 366; uint constant CTX0_DATA_START = 366; // CTX1 Liquidity Swap Payload ************************************************************************************************** uint constant CTX1_MIN_VAULT_TOKEN_START = 228; uint constant CTX1_MIN_VAULT_TOKEN_END = 260; uint constant CTX1_MIN_REFERENCE_START = 260; uint constant CTX1_MIN_REFERENCE_END = 292; uint constant CTX1_FROM_AMOUNT_START = 292; uint constant CTX1_FROM_AMOUNT_END = 324; uint constant CTX1_BLOCK_NUMBER_START = 324; uint constant CTX1_BLOCK_NUMBER_END = 328; uint constant CTX1_DATA_LENGTH_START = 328; uint constant CTX1_DATA_LENGTH_END = 330; uint constant CTX1_DATA_START = 330;
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMessageEscrowErrors {
error AckHasNotBeenExecuted(); // 0x3d1553f8
error CannotRetryWrongMessage(bytes32,bytes32); // 0x48ce7fac
error DeadlineInPast(uint64 blocktimestamp, uint64 actual); // 0x2d098d59
error DeadlineNotPassed(uint64 expected, uint64 actual); // 0x862c57f4
error DeadlineTooLong(uint64 maxAllowed, uint64 actual); // 0x3c06f369
error FeeRecipientIsZero(); // 0xfc53a835
error ImplementationAddressAlreadySet(bytes currentImplementation); // 0xdba47850
error IncorrectValueProvided(uint128 expected, uint128 actual); // 0x0b52a60b
error InvalidImplementationAddress(); // 0xc970156c
error InvalidTimeoutPackage(bytes32 expected, bytes32 actual); // 0xe020885d
error MessageAlreadyAcked(); // 0x8af35858
error MessageAlreadyBountied(); // 0x068a62ee
error MessageAlreadyProcessed(); // 0x7b042609
error MessageAlreadySpent(); // 0xe954aba2
error MessageDoesNotExist(); // 0x970e41ec
error MessageHasInvalidContext(); // 0x3fcdbaba
error NoImplementationAddressSet(); // 0x9f994b4b
error NotEnoughGasExecution(); // 0x6bc33587
error NotEnoughGasProvided(uint128 expected, uint128 actual); // 0x030748b5
error NotImplementedError(); // 0xd41c17e7
error RefundGasToIsZero(); // 0x6a1a6afe
error RouteDisabled(); // 0x17d0b6db
error SendLostGasToIsZero(); // 0x9c76a9df
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IMessageEscrowStructs } from "./IMessageEscrowStructs.sol";
interface IMessageEscrowEvents {
// Important notice for relayers. The implementations (sourceImplementation and destinationImplementation),
// when indexed in events, the hash is in the topic not the actual implementation.
event BountyPlaced(
bytes indexed destinationImplementation,
bytes32 chainIdentifier,
bytes32 indexed messageIdentifier,
IMessageEscrowStructs.IncentiveDescription incentive
);
event MessageDelivered(bytes indexed sourceImplementation, bytes32 chainIdentifier, bytes32 indexed messageIdentifier);
event MessageAcked(bytes destinationImplementation, bytes32 chainIdentifier, bytes32 messageIdentifier); // Not indexed since relayers can sort by BountyClaimed.
event TimeoutInitiated(bytes sourceImplementation, bytes32 chainIdentifier, bytes32 messageIdentifier);
event MessageTimedOut(bytes destinationImplementation, bytes32 chainIdentifier, bytes32 messageIdentifier); // Not indexed since relayers can sort by BountyClaimed.
event BountyClaimed(
bytes indexed destinationImplementation,
bytes32 chainIdentifier,
bytes32 indexed messageIdentifier,
uint64 gasSpentOnDestination,
uint64 gasSpentOnSource,
uint128 destinationRelayerReward,
uint128 sourceRelayerReward
);
// To save gas, this event does not emit the full incentive scheme.
// Instead, the new gas prices are emitted. As a result, the relayer can collect all bountyIncreased
// and then use the maximum. (since the maxmimum is enforced in the smart contract)
event BountyIncreased(
bytes32 indexed messageIdentifier,
uint96 newDeliveryGasPrice,
uint96 newAckGasPrice
);
event RemoteImplementationSet(address application, bytes32 chainIdentifier, bytes32 implementationAddressHash, bytes implementationAddress);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface ICrossChainReceiver {
/**
* @notice Handles the acknowledgement from the destination
* @dev acknowledgement is exactly the output of receiveMessage except if receiveMessage failed, then it is error code (0xff or 0xfe) + original message.
* If an acknowledgement isn't needed, this can be implemented as {}.
* - This function can be called by someone else again! Ensure that if this endpoint is called twice with the same message nothing bad happens.
* - If the application expects that the maxGasAck will be provided, then it should check that it got enough and revert if it didn't.
* Otherwise, it is assumed that you didn't need the extra gas.
* @param destinationIdentifier An identifier for the destination chain.
* @param messageIdentifier A unique identifier for the message. The identifier matches the identifier returned when escrowed the message.
* This identifier can be mismanaged by the messaging protocol.
* @param acknowledgement The acknowledgement sent back by receiveMessage. Is 0xff if receiveMessage reverted.
*/
function receiveAck(bytes32 destinationIdentifier, bytes32 messageIdentifier, bytes calldata acknowledgement) external;
/**
* @notice receiveMessage from a cross-chain call.
* @dev The application needs to check the fromApplication combined with sourceIdentifierbytes to figure out if the call is authenticated.
* - If the application expects that the maxGasDelivery will be provided, then it should check that it got enough and revert if it didn't.
* Otherwise, it is assumed that you didn't need the extra gas.
* @return acknowledgement Information which is passed to receiveAck.
* If you return 0xff, you cannot know the difference between Executed but "failed" and outright failed.
*/
function receiveMessage(bytes32 sourceIdentifierbytes, bytes32 messageIdentifier, bytes calldata fromApplication, bytes calldata message) external returns(bytes memory acknowledgement);
}//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/**
* @title Escrow related functions defined by Catalyst v1 Vaults
* @notice Contains the functions used to manage escrows by the cross-chain interface.
*/
interface ICatalystV1VaultSuccessFailure {
/** @notice Release the escrowed tokens into the vault. */
function onSendAssetSuccess(
bytes32 channelId,
bytes calldata toAccount,
uint256 U,
uint256 escrowAmount,
address escrowToken,
uint32 blockNumberMod
) external;
/** @notice Returned the escrowed tokens to the user */
function onSendAssetFailure(
bytes32 channelId,
bytes calldata toAccount,
uint256 U,
uint256 escrowAmount,
address escrowToken,
uint32 blockNumberMod
) external;
/** @notice Release the escrowed tokens into the vault. */
function onSendLiquiditySuccess(
bytes32 channelId,
bytes calldata toAccount,
uint256 U,
uint256 escrowAmount,
uint32 blockNumberMod
) external;
/** @notice Returned the escrowed tokens to the user */
function onSendLiquidityFailure(
bytes32 channelId,
bytes calldata toAccount,
uint256 U,
uint256 escrowAmount,
uint32 blockNumberMod
) external;
}//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/**
* @title Administrative actions defined by Catalyst v1 Vaults
* @notice Contains all functions which can only be called by privileged users.
*/
interface ICatalystV1VaultAdministration {
function setFeeAdministrator(address administrator) external;
function setVaultFee(uint64 fee) external;
function setGovernanceFee(uint64 fee) external;
/**
* @notice Initializes the vault pricing parameters.
* @param assets The list of assets the vault will support.
* @param weights The weights of the tokens.
* @param amp Vault amplification.
* @param depositor The account to which the initial vault tokens are minted to.
*/
function initializeSwapCurves(
address[] calldata assets,
uint256[] calldata weights,
uint64 amp,
address depositor
) external;
/**
* @notice Creates a connection to the vault toVault on the channel _channelId.
* @dev if _vaultReceiving is an EVM vault, it can be computes as:
* Vyper: convert(_vaultAddress, bytes32)
* Solidity: abi.encode(_vaultAddress)
* Brownie: brownie.convert.to_bytes(_vaultAddress, type_str="bytes32")
* setupMaster == ZERO_ADDRESS
* @param channelId The _channelId of the target vault.
* @param toVault The bytes32 representation of the target vault
* @param state Boolean indicating if the connection should be open or closed.
*/
function setConnection(
bytes32 channelId,
bytes calldata toVault,
bool state
) external;
/**
* @notice Gives up short term ownership of the vault. This makes the vault unstoppable.
*/
function finishSetup() external;
}//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/**
* @title Derived Vault state
* @notice Contains all vault state which is derived from vault storage
*/
interface ICatalystV1VaultDerived {
/** @notice Returns the current cross-chain unit capacity. */
function getUnitCapacity() external view returns (uint256);
function calcSendAsset(address from, uint256 amount) external view returns (uint256);
/**
* @notice Computes the output of ReceiveAsset, without executing one.
* @param to The address of the token to buy.
* @param U The number of units used to buy to.
* @return uint256 Number of purchased tokens.
*/
function calcReceiveAsset(address to, uint256 U) external view returns (uint256);
/**
* @notice Computes the output of localSwap, without executing one.
* @param from The address of the token to sell.
* @param to The address of the token to buy.
* @param amount The amount of from token to sell for to token.
* @return Output denominated in to token.
*/
function calcLocalSwap(address from, address to, uint256 amount) external view returns (uint256);
}//SPDX-License-Identifier: MIT pragma solidity ^0.8.17; error EscrowAlreadyExists(); // 0xed778779 error ExceedsSecurityLimit(); // 0x7c1e66d0 error NotEnoughGas(); // 0xdd629f86 error ReturnInsufficient(uint256,uint256); // 0x24557f05 error UnusedUnitsAfterWithdrawal(uint256); // 0x0289311f error VaultNotConnected(); // 0x2c64c1b2 error WithdrawRatioNotZero(); // 0xb8003bfa
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/**
* @title Events emitted by Catalyst v1 Vaults
* @notice Contains all events emitted by the vault
* @dev When using events to match transations, the combination of: channelId, fromVault, toAccount, toAsset, units, and block number is semi-guranteed to be unique.
* If more than 2**32 blocks exist, then all instances are guaranteed to be non-overlapping
*/
interface ICatalystV1VaultEvents {
/**
* @notice Describes an atomic swap between the 2 tokens: _fromAsset and _toAsset.
* @param account The user / exchange who facilitated the trade (msg.sender)
* @param fromAsset The asset which was sold in exchange for _toAsset
* @param toAsset The asset which was purchased with _fromAsset
* @param fromAmount The number of _fromAsset sold
* @param toAmount The number of tokens provided to toAccount
*/
event LocalSwap(
address indexed account,
address fromAsset,
address toAsset,
uint256 fromAmount,
uint256 toAmount
);
/**
* @notice Describes the creation of an external swap: Cross-chain swap.
* @param channelId The target chain identifier
* @param toVault The target vault.
* @param toAccount The recipient of the trade. The person who bought the trade is not present.
* @param fromAsset The asset which was sold in exchange for _toAsset.
* @param toAssetIndex The token index of the asset to purchase on _toChain.
* @param fromAmount The number of _fromAsset sold.
* @param minOut The minimum output to be accepted of fromAsset.
* @param units The calculated number of units bought. Will be sold to buy _toAsset
* @param underwriteIncentiveX16 The incentive out of 2**16 - 1 provided to the underwriter.
* @param fee The number of tokens paid to the vault in fees.
*/
event SendAsset(
bytes32 channelId,
bytes toVault,
bytes toAccount,
address fromAsset,
uint8 toAssetIndex,
uint256 fromAmount,
uint256 minOut,
uint256 units,
uint256 fee,
uint16 underwriteIncentiveX16
);
/**
* @notice Describes the arrival of an external swap: Cross-chain swap.
* If toAccount is used to match trades, remember to convert it into 64 + 1 bytes.
* @param channelId The target chain identifier
* @param fromVault The source vault.
* @param toAccount The recipient of the trade.
* @param toAsset The asset which was purchased with _fromAsset
* @param units The number of units sent from the other chain.
* @param toAmount The number of tokens provided to toAccount
* @param fromAmount The amount spent to get units on the source side.
* @param fromAsset The provided asset on the source side.
* @param sourceBlockNumberMod The block number of the sending transaction mod 2**32 - 1
*/
event ReceiveAsset(
bytes32 channelId,
bytes fromVault,
address toAccount,
address toAsset,
uint256 units,
uint256 toAmount,
uint256 fromAmount,
bytes fromAsset,
uint32 sourceBlockNumberMod
);
/**
* @notice Describes the creation of a liquidity swap
* @param channelId The target chain identifier
* @param toVault The target vault.
* @param toAccount The recipient of the liquidity. The person who bought the trade is not present.
* @param fromAmount The number of _fromAsset sold
* @param minOut An array containing a list of minimum outputs [minVaultTokens, minReferenceAssets]
* @param units The calculated number of liquidity units bought.
*/
event SendLiquidity(
bytes32 channelId,
bytes toVault,
bytes toAccount,
uint256 fromAmount,
uint256[2] minOut,
uint256 units
);
/**
* @notice Describes the arrival of a liquidity swap
* @param channelId The target chain identifier
* @param fromVault The source vault.
* @param toAccount The recipient of the liquidity.
* @param units The number of liquidity units sent from the other chain.
* @param toAmount The number of vault tokens provided to toAccount
* @param fromAmount The amount spent to get units on the source side.
* @param sourceBlockNumberMod The block number of the sending transaction mod 2**32 - 1
*/
event ReceiveLiquidity(
bytes32 channelId,
bytes fromVault,
address toAccount,
uint256 units,
uint256 toAmount,
uint256 fromAmount,
uint256 sourceBlockNumberMod
);
/**
* @notice Emitted on liquidity deposits.
* @param toAccount The depositor. Is credited with _mints vault tokens.
* @param mint The number of minted vault tokens credited to toAccount
* @param assets An array of the number of deposited assets.
*/
event VaultDeposit(address indexed toAccount, uint256 mint, uint256[] assets);
/**
* @notice Emitted on liquidity withdrawal.
* @param toAccount The withdrawer. Is debited _burns vault tokens.
* @param burn The number of burned vault tokens.
* @param assets An array of the token amounts returned
*/
event VaultWithdraw(address indexed toAccount, uint256 burn, uint256[] assets);
/** @notice Called upon successful asset swap. */
event SendAssetSuccess(
bytes32 channelId,
bytes toAccount,
uint256 units,
uint256 escrowAmount,
address escrowToken,
uint32 blockNumberMod
);
/** @notice Called upon failed asset swap. */
event SendAssetFailure(
bytes32 channelId,
bytes toAccount,
uint256 units,
uint256 escrowAmount,
address escrowToken,
uint32 blockNumberMod
);
/** @notice Called upon successful liquidity swap. */
event SendLiquiditySuccess(
bytes32 channelId,
bytes toAccount,
uint256 units,
uint256 escrowAmount,
uint32 blockNumberMod
);
/** @notice Called upon failed liquidity swap. */
event SendLiquidityFailure(
bytes32 channelId,
bytes toAccount,
uint256 units,
uint256 escrowAmount,
uint32 blockNumberMod
);
/** @notice Vault setup has been finalised. */
event FinishSetup();
/**
* @notice Emitted on fee administrator adjustment
* @param administrator The new vault fee administrator
*/
event SetFeeAdministrator(
address administrator
);
/**
* @notice Emitted on vault fee adjustment
* @param fee The new vault fee
*/
event SetVaultFee(
uint64 fee
);
/**
* @notice Emitted on governance fee adjustment
* @param fee The new governance fee
*/
event SetGovernanceFee(
uint64 fee
);
/**
* @notice Emitted on weights modification
* @param targetTime Time at which the weights adjustment must complete.
* @param targetWeights The desired new weights.
*/
event SetWeights(
uint248 targetTime,
uint256[] targetWeights
);
/**
* @notice Amplification has been modification
* @param targetTime Time at which the amplification adjustment must complete.
* @param targetAmplification The desired new amplification.
*/
event SetAmplification(
uint48 targetTime,
uint256 targetAmplification
);
/**
* @notice A connection has been modified
* @param channelId Target chain identifier.
* @param toVault Bytes32 representation of the target vault.
* @param newState Boolean indicating if the connection should be open or closed.
*/
event SetConnection(
bytes32 channelId,
bytes toVault,
bool newState
);
//-- Underwriting Events --//
/**
* @notice A swap has been underwritten.
*/
event SwapUnderwritten(
bytes32 indexed identifier,
address toAsset,
uint256 U,
uint256 purchasedTokens
);
}//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/**
* @title Immutable vault state
* @notice Contains all vault state which doesn't change once set.
*/
interface ICatalystV1VaultImmutables {
function _chainInterface() external view returns (address);
function FACTORY() external view returns (address);
function MATHLIB() external view returns (address);
/**
* @notice To indicate which token is desired on the target vault,the _toAsset is an integer
* from 0 to MAX_ASSETS indicating which asset the vault should purchase with units.
*/
function _tokenIndexing(uint256 tokenIndex) external view returns (address);
}//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { ICatalystV1Structs } from "./ICatalystV1VaultState.sol";
interface ICatalystV1VaultPermissionless {
/**
* @notice Setup a vault.
* @param name_ Name of the Vault token.
* @param symbol_ Symbol for the Vault token.
* @param chainInterface Cross chain interface used for cross-chain swaps. (Can be address(0) to disable cross-chain swaps.)
* @param vaultFee Vault fee.
* @param governanceFee Governance fee share.
* @param feeAdministrator Account that can modify the fees.
* @param setupMaster Short-term owner of the vault (until finishSetup is called).
*/
function setup(
string calldata name_,
string calldata symbol_,
address chainInterface,
uint64 vaultFee,
uint64 governanceFee,
address feeAdministrator,
address setupMaster
) external;
//--- Balance Changes ---//
/**
* @notice Deposits a user configurable amount of tokens.
* @dev Requires approvals for all deposited tokens within the vault.
* Volatile: It is advised that the deposit matches the vault's %token distribution.
* Amplified: It is advised that the deposit is as close to 1,1,... as possible.
* Otherwise between 1,1,... and the vault's %token distribution.
* @param tokenAmounts Array of the tokens amounts to be deposited.
* @param minOut Minimum number of vault tokens to be minted.
*/
function depositMixed(uint256[] calldata tokenAmounts, uint256 minOut)
external returns(uint256);
/**
* @notice Burns baseAmount and releases the symmetrical share
* of tokens to the burner. This doesn't change the vault price.
* @param baseAmount Number of vault tokens to burn.
*/
function withdrawAll(uint256 baseAmount, uint256[] calldata minOut)
external returns(uint256[] memory);
/**
* @notice Burns vaultTokens and release a token distribution which can be set by the user.
* @dev Requires approvals for all tokens within the vault.
* Volatile: It is advised that the deposit matches the vault's %token distribution.
* Amplified: It is advised that the deposit matches the vault's %token distribution.
* Otherwise it should be weighted towards the tokens the vault has more of.
* @param vaultTokens Number of vault tokens to withdraw.
* @param withdrawRatio Percentage of units used to withdraw. In the following special scheme: U_0 = U · withdrawRatio[0], U_1 = (U - U_0) · withdrawRatio[1], U_2 = (U - U_0 - U_1) · withdrawRatio[2], .... Is WAD.
* @param minOut Minimum number of tokens minted.
*/
function withdrawMixed(
uint256 vaultTokens,
uint256[] calldata withdrawRatio,
uint256[] calldata minOut
) external returns(uint256[] memory);
//--- Swaps ---//
/**
* @notice A swap between 2 assets which both are inside the vault. Is atomic.
* @param fromAsset Asset the user wants to sell.
* @param toAsset Asset the user wants to buy.
* @param amount Amount of fromAsset the user wants to sell.
* @param minOut Minimum output of _toAsset the user wants.
*/
function localSwap(
address fromAsset,
address toAsset,
uint256 amount,
uint256 minOut
) external returns (uint256);
/**
* @notice Initiate a cross-chain swap by purchasing units and transfer them to another vault.
* @dev Addresses are encoded in 64 + 1 bytes. To encode for EVM, encode as:
* Solidity: abi.encodePacket(uint8(20), bytes32(0), abi.encode(<vaultAddress>))
* @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive.
* @param fromAsset Asset the user wants to sell.
* @param toAssetIndex Index of the asset the user wants to buy in the target vault.
* @param amount Number of fromAsset to sell to the vault.
* @param minOut Minimum number of returned tokens to the toAccount on the target chain.
* @param fallbackUser If the transaction fails, send the escrowed funds to this address.
* @param underwriteIncentiveX16 Payment for underwriting the swap (out of type(uint16.max))
* @param calldata_ Data field if a call should be made on the target chain.
* Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(<address>), <data>). At maximum 65535 bytes can be passed.
* @return uint256 The number of units minted.
*/
function sendAsset(
ICatalystV1Structs.RouteDescription calldata routeDescription,
address fromAsset,
uint8 toAssetIndex,
uint256 amount,
uint256 minOut,
address fallbackUser,
uint16 underwriteIncentiveX16,
bytes calldata calldata_
) external payable returns (uint256);
/**
* @notice Initiate a cross-chain swap by purchasing units and transfer them to another vault using a fixed number of units.
* @dev Addresses are encoded in 64 + 1 bytes. To encode for EVM, encode as:
* Solidity: abi.encodePacket(uint8(20), bytes32(0), abi.encode(<vaultAddress>))
* @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive.
* @param fromAsset Asset the user wants to sell.
* @param toAssetIndex Index of the asset the user wants to buy in the target vault.
* @param amount Number of fromAsset to sell to the vault.
* @param minOut Minimum number of returned tokens to the toAccount on the target chain.
* @param minU Minimum and exact number of units sent.
* @param fallbackUser If the transaction fails, send the escrowed funds to this address.
* @param calldata_ Data field if a call should be made on the target chain.
* Encoding depends on the target chain, with evm being: abi.encodePacket(bytes20(<address>), <data>)
* @param underwriteIncentiveX16 Payment for underwriting the swap (out of type(uint16.max))
* @return uint256 Number of units minted.
*/
function sendAssetFixedUnit(
ICatalystV1Structs.RouteDescription calldata routeDescription,
address fromAsset,
uint8 toAssetIndex,
uint256 amount,
uint256 minOut,
uint256 minU,
address fallbackUser,
uint16 underwriteIncentiveX16,
bytes calldata calldata_
) external payable returns (uint256);
/**
* @notice Completes a cross-chain swap by converting units to the desired token (toAsset)
* @dev Can only be called by the chainInterface.
* @param channelId The incoming connection identifier.
* @param fromVault The source vault.
* @param toAssetIndex Index of the asset to be purchased with Units.
* @param toAccount The recipient.
* @param U Number of units to convert into toAsset.
* @param minOut Minimum number of tokens bought. Reverts if less.
* @param fromAmount Used to match cross-chain swap events. The input amount on the sending chain.
* @param fromAsset Used to match cross-chain swap events. The input asset on the sending chain.
* @param blockNumberMod Used to match cross-chain swap events. The block number from the host side.
*/
function receiveAsset(
bytes32 channelId,
bytes calldata fromVault,
uint256 toAssetIndex,
address toAccount,
uint256 U,
uint256 minOut,
uint256 fromAmount,
bytes calldata fromAsset,
uint32 blockNumberMod
) external returns(uint256 purchasedTokens);
/**
* @notice Initiate a cross-chain liquidity swap by withdrawing tokens and converting them to units.
* @dev While the description says tokens are withdrawn and then converted to units, vault tokens are converted
* directly into units through the following equation:
* U = ln(PT/(PT-pt)) * \sum W_i
* @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive.
* @param vaultTokens Number of vault tokens to exchange.
* @param minOut Array of minout describing: [the minimum number of vault tokens, the minimum number of reference assets].
* @param fallbackUser If the transaction fails, send the escrowed funds to this address.
* @param calldata_ Data field if a call should be made on the target chain.
* Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(<address>), <data>). At maximum 65535 bytes can be passed.
* @return uint256 Number of units minted.
*/
function sendLiquidity(
ICatalystV1Structs.RouteDescription calldata routeDescription,
uint256 vaultTokens,
uint256[2] calldata minOut,
address fallbackUser,
bytes calldata calldata_
) external payable returns (uint256);
/**
* @notice Completes a cross-chain liquidity swap by converting units to tokens and depositing.
* @dev Called exclusively by the chainInterface.
* @param fromVault Source vault
* @param toAccount Recipient of the vault tokens
* @param U Number of units to convert into vault tokens.
* @param minVaultTokens Minimum number of vault tokens to mint on target vault. Otherwise: Reject
* @param minReferenceAsset Minimum number of reference asset the vaults tokens are worth. Otherwise: Reject
* @param fromAmount Used to match cross-chain swap events. The input amount on the sending chain.
* @param blockNumberMod Used to match cross-chain swap events. The block number from the host side.
*/
function receiveLiquidity(
bytes32 channelId,
bytes calldata fromVault,
address toAccount,
uint256 U,
uint256 minVaultTokens,
uint256 minReferenceAsset,
uint256 fromAmount,
uint32 blockNumberMod
) external returns(uint256 purchasedVaultTokens);
}//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/** @title Extensions to vaults which supports underwriting. */
interface ICatalystV1Underwriting {
function underwriteAsset(
bytes32 identifier,
address toAsset,
uint256 U,
uint256 minOut
) external returns (uint256);
function releaseUnderwriteAsset(
address refundTo,
bytes32 identifier,
uint256 escrowAmount,
address escrowToken,
bytes32 sourceIdentifier,
bytes calldata fromVault
) external;
function deleteUnderwriteAsset(
bytes32 identifier,
uint256 U,
uint256 escrowAmount,
address escrowToken
) external;
}{
"remappings": [
"forge-std/=lib/forge-std/src/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"solmate/=lib/solmate/src/",
"GeneralisedIncentives/=lib/GeneralisedIncentives/",
"@lazyledger/protobuf3-solidity-lib/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/protobuf3-solidity-lib/",
"@openzeppelin-upgradeable/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/openzeppelin-contracts-upgradeable/",
"@openzeppelin/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/openzeppelin-contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"base64/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/base64/",
"clones-with-immutable-args/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/optimism/packages/contracts-bedrock/lib/clones-with-immutable-args/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"openzeppelin-contracts-upgradeable/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin/=lib/GeneralisedIncentives/lib/openzeppelin-contracts/contracts/",
"optimism/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/",
"proto/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/proto/",
"protobuf3-solidity-lib/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/protobuf3-solidity-lib/contracts/",
"safe-contracts/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/optimism/packages/contracts-bedrock/lib/safe-contracts/contracts/",
"solady/=lib/solady/src/",
"solmate/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/optimism/packages/contracts-bedrock/lib/solmate/src/",
"vibc-core-smart-contracts/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 10000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
},
"evmVersion": "paris",
"viaIR": true,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"GARP_","type":"address"},{"internalType":"address","name":"defaultOwner","type":"address"}],"stateMutability":"payable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"ChainAlreadySetup","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidBytes65Address","type":"error"},{"inputs":[],"name":"InvalidCaller","type":"error"},{"inputs":[{"internalType":"bytes1","name":"context","type":"bytes1"}],"name":"InvalidContext","type":"error"},{"inputs":[],"name":"InvalidSourceApplication","type":"error"},{"inputs":[],"name":"MaxUnderwriteDurationTooLong","type":"error"},{"inputs":[],"name":"MaxUnderwriteDurationTooShort","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NoVaultConnection","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"name":"NotEnoughIncentives","type":"error"},{"inputs":[],"name":"SwapAlreadyUnderwritten","type":"error"},{"inputs":[],"name":"SwapRecentlyUnderwritten","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"}],"name":"UnderwriteDoesNotExist","type":"error"},{"inputs":[{"internalType":"uint256","name":"blocksUnitilExpiry","type":"uint256"}],"name":"UnderwriteNotExpired","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"identifier","type":"bytes32"},{"indexed":false,"internalType":"address","name":"expirer","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"ExpireUnderwrite","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"identifier","type":"bytes32"}],"name":"FulfillUnderwrite","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newMaxUnderwriteDuration","type":"uint256"}],"name":"MaxUnderwriteDuration","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"identifier","type":"bytes32"},{"indexed":false,"internalType":"uint48","name":"minGas","type":"uint48"}],"name":"MinGasFor","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"remoteCCI","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"remoteGARP","type":"bytes"}],"name":"RemoteImplementationSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes1","name":"error","type":"bytes1"}],"name":"SwapFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"identifier","type":"bytes32"},{"indexed":true,"internalType":"address","name":"underwriter","type":"address"},{"indexed":false,"internalType":"uint96","name":"expiry","type":"uint96"},{"indexed":false,"internalType":"address","name":"targetVault","type":"address"},{"indexed":false,"internalType":"address","name":"toAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"U","type":"uint256"},{"indexed":false,"internalType":"address","name":"toAccount","type":"address"},{"indexed":false,"internalType":"uint256","name":"outAmount","type":"uint256"}],"name":"SwapUnderwritten","type":"event"},{"inputs":[],"name":"EXPIRE_CALLER_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EXPIRE_CALLER_REWARD_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GARP","outputs":[{"internalType":"contract IIncentivizedMessageEscrow","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNDERWRITING_COLLATERAL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNDERWRITING_COLLATERAL_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"chainIdentifierToDestinationAddress","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"internalType":"bytes","name":"remoteCCI","type":"bytes"},{"internalType":"bytes","name":"remoteGARP","type":"bytes"}],"name":"connectNewChain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"evmAddress","type":"address"}],"name":"convertEVMTo65","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"estimateAdditionalCost","outputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"targetVault","type":"address"},{"internalType":"address","name":"toAsset","type":"address"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"address","name":"toAccount","type":"address"},{"internalType":"uint16","name":"underwriteIncentiveX16","type":"uint16"},{"internalType":"bytes","name":"cdata","type":"bytes"}],"name":"expireUnderwrite","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"targetVault","type":"address"},{"internalType":"address","name":"toAsset","type":"address"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"address","name":"toAccount","type":"address"},{"internalType":"uint16","name":"underwriteIncentiveX16","type":"uint16"},{"internalType":"bytes","name":"cdata","type":"bytes"}],"name":"getUnderwriteIdentifier","outputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"maxUnderwritingDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"minGasFor","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"destinationIdentifier","type":"bytes32"},{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes","name":"acknowledgement","type":"bytes"}],"name":"receiveAck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"sourceIdentifier","type":"bytes32"},{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes","name":"fromApplication","type":"bytes"},{"internalType":"bytes","name":"message","type":"bytes"}],"name":"receiveMessage","outputs":[{"internalType":"bytes","name":"acknowledgement","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"internalType":"bytes","name":"toVault","type":"bytes"},{"internalType":"bytes","name":"toAccount","type":"bytes"},{"components":[{"internalType":"uint48","name":"maxGasDelivery","type":"uint48"},{"internalType":"uint48","name":"maxGasAck","type":"uint48"},{"internalType":"address","name":"refundGasTo","type":"address"},{"internalType":"uint96","name":"priceOfDeliveryGas","type":"uint96"},{"internalType":"uint96","name":"priceOfAckGas","type":"uint96"},{"internalType":"uint64","name":"targetDelta","type":"uint64"}],"internalType":"struct IMessageEscrowStructs.IncentiveDescription","name":"incentive","type":"tuple"},{"internalType":"uint64","name":"deadline","type":"uint64"}],"internalType":"struct ICatalystV1Structs.RouteDescription","name":"routeDescription","type":"tuple"},{"internalType":"uint8","name":"toAssetIndex","type":"uint8"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"address","name":"fromAsset","type":"address"},{"internalType":"uint16","name":"underwriteIncentiveX16","type":"uint16"},{"internalType":"bytes","name":"calldata_","type":"bytes"}],"name":"sendCrossChainAsset","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"internalType":"bytes","name":"toVault","type":"bytes"},{"internalType":"bytes","name":"toAccount","type":"bytes"},{"components":[{"internalType":"uint48","name":"maxGasDelivery","type":"uint48"},{"internalType":"uint48","name":"maxGasAck","type":"uint48"},{"internalType":"address","name":"refundGasTo","type":"address"},{"internalType":"uint96","name":"priceOfDeliveryGas","type":"uint96"},{"internalType":"uint96","name":"priceOfAckGas","type":"uint96"},{"internalType":"uint64","name":"targetDelta","type":"uint64"}],"internalType":"struct IMessageEscrowStructs.IncentiveDescription","name":"incentive","type":"tuple"},{"internalType":"uint64","name":"deadline","type":"uint64"}],"internalType":"struct ICatalystV1Structs.RouteDescription","name":"routeDescription","type":"tuple"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256[2]","name":"minOut","type":"uint256[2]"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"bytes","name":"calldata_","type":"bytes"}],"name":"sendCrossChainLiquidity","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMaxUnderwriteDuration","type":"uint256"}],"name":"setMaxUnderwritingDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"internalType":"uint48","name":"minGas","type":"uint48"}],"name":"setMinGasFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"thisBytes65","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"targetVault","type":"address"},{"internalType":"address","name":"toAsset","type":"address"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"address","name":"toAccount","type":"address"},{"internalType":"uint16","name":"underwriteIncentiveX16","type":"uint16"},{"internalType":"bytes","name":"cdata","type":"bytes"}],"name":"underwrite","outputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"sourceIdentifier","type":"bytes32"},{"internalType":"bytes","name":"fromVault","type":"bytes"},{"internalType":"address","name":"targetVault","type":"address"},{"internalType":"address","name":"toAsset","type":"address"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"address","name":"toAccount","type":"address"},{"internalType":"uint16","name":"underwriteIncentiveX16","type":"uint16"},{"internalType":"bytes","name":"cdata","type":"bytes"}],"name":"underwriteAndCheckConnection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"underwritingStorage","outputs":[{"internalType":"uint256","name":"tokens","type":"uint256"},{"internalType":"address","name":"refundTo","type":"address"},{"internalType":"uint96","name":"expiry","type":"uint96"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60a0601f620037d338819003918201601f19168301916001600160401b038311848410176200011857808492604094855283398101031262000113576200005460206200004c836200012e565b92016200012e565b613840600281905590916001600160a01b039081169190821562000113577f88e95e1ea5dc9ffb42b44903f6dc9ba92e46f0ae1931bd6e6cbeecdf3d6b9a02936020936080521680638b78c6d8195560007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a3604051908152a160405161368f9081620001448239608051818181610b0d01528181610d1a01528181611072015281816112ca0152818161135f015281816118a10152611a2d0152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620001135756fe60a060408181526004918236101561001657600080fd5b600092833560e01c91826311cbf805146119e3575081631e2b8990146119c6578163256929621461197b578163318987c4146116035781634f37a457146115d35781635330a5de1461152057816354d1f13d146114da578163715018a614611479578163898e68ad1461145d5781638da5cb5b1461140957816392006e731461130d57816394aaf73f146112ee578163989e34b31461129d578163ac23728114610f14578163b40f969a14610f19578163ba3c99e614610f14578163bf09469414610eef578163ca9b7b2f14610e5c578163d00689b614610caf578163d6512967146107de578163d94501871461078b578163daf1e1671461076c578163dbaed76b146105d7578163dd4aeedd146103a2578163e5a7783514610376578163e73ae76414610326578163ea901f1a146102e8578163f04e283e1461023c578163f2fde38b146101a5575063fee81cf41461016f57600080fd5b346101a15760206003193601126101a15760209161018b611ad1565b9063389a75e1600c525281600c20549051908152f35b5080fd5b839060206003193601126101a1576101bb611ad1565b906101c4612a94565b8160601b15610231575073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a35580f35b637448fbae8352601cfd5b8360206003193601126102e557610251611ad1565b610259612a94565b63389a75e1600c528082526020600c2092835442116102da57508173ffffffffffffffffffffffffffffffffffffffff929355167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a35580f35b636f5e88188352601cfd5b80fd5b5050346101a15760206003193601126101a1576103229061030f61030a611ad1565b6125fd565b9051918291602083526020830190611bae565b0390f35b90503461037257602060031936011261037257906103608161032294610359943581528060205220825193848092611c44565b0383611d57565b51918291602083526020830190611bae565b8280fd5b5050346101a15760209061039b61038c36611af4565b96959095949194939293612135565b9051908152f35b91905034610372576103c9906103b736611af4565b928699948684979c988a959495612acc565b9586885260036020528488209260018401549373ffffffffffffffffffffffffffffffffffffffff908186169081156105a857908b94939291549660a01c90330361052a575b508984526003602052600060018986208281550155808716803b15610526578492836084928d948c5197889687957fb9ff629b00000000000000000000000000000000000000000000000000000000875286015260248501528a60448501528d1660648401525af1801561051c576104fc575b50507f5d002c935f79d7b578b7c23b1101d4508bb3bd36e5b53c2aa00263ee5ac9f0de9461ffff936104f695936104e09360236103e897889216830260101c920204019461015e86020480956104d9823386612c5a565b0391612c5a565b5133815260208101919091529081906040820190565b0390a280f35b6105099095939495611cf8565b61051857863894939294610482565b8680fd5b86513d84823e3d90fd5b8480fd5b909192935043811161054057908a93929161040f565b82889143810390811161057a5760249251917f62141db5000000000000000000000000000000000000000000000000000000008352820152fd5b6011827f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b6024848c8b51917fae029d69000000000000000000000000000000000000000000000000000000008352820152fd5b8383346101a1576101206003193601126101a15767ffffffffffffffff6024358181116107685761060b9036908601611a86565b91906044359073ffffffffffffffffffffffffffffffffffffffff9182811695868203610763576064359284841684036107635760c43594851685036107635760e4359561ffff87168703610763576101043590811161075f576106be916106788c602093369101611a86565b9a90998d8651958694859384937f66ae79e600000000000000000000000000000000000000000000000000000000855280359085015289602485015260448401916120af565b03915afa908115610755578991610716575b50156106ee57506106ea96975060a4359160843591612135565b5080f35b8890517fea66ca6d000000000000000000000000000000000000000000000000000000008152fd5b90506020813d60201161074d575b8161073160209383611d57565b8101031261074957518015158103610749578a6106d0565b8880fd5b3d9150610724565b82513d8b823e3d90fd5b8980fd5b600080fd5b8380fd5b5050346101a157816003193601126101a1576103229061030f306125fd565b9050346103725760206003193601126103725760609282913581526003602052209060018254920154815192835273ffffffffffffffffffffffffffffffffffffffff8116602084015260a01c90820152f35b6003198484610100368401126101a15783359367ffffffffffffffff80861161076857610140868301958736030112610768576024359560ff871687036105265760a4359273ffffffffffffffffffffffffffffffffffffffff928385168095036107635760c4359061ffff821682036107635760e435908111610cab57908899610870899a98999336908601611a86565b9290918a61089d60648901809c3597888152600160205265ffffffffffff80948192205416928392611e94565b1610610c9d5750808c8e8080526001602052205416908160848a01916108c283611e94565b1610610c4b57505060e487016108d781611ea7565b600b3a023a8104600b143a151715610c1f57600a900491906bffffffffffffffffffffffff168211610bc3575050908c9291604161091960448a018096611f02565b905003610b9b578d604161093160248b018093611f02565b905003610b7357918e8e9f928e9f979695939e809e9f889661095291611f02565b9761095d9194611f02565b90818984519a8b976020890160805260805137860191602083018d8152370191604435602084015260f81b7fff00000000000000000000000000000000000000000000000000000000000000169082015260643560418201526084356061820152036061810185526081016109d29085611d57565b7fffff00000000000000000000000000000000000000000000000000000000000080918d519c888e9960208b01527f1400000000000000000000000000000000000000000000000000000000000000978860218c01528160228c01523360428c01525160628b018160805191610a4792611b8b565b8a01976062890152606388015260838701524360e01b7fffffffff000000000000000000000000000000000000000000000000000000001660a387015260f01b1660a78501528260f01b1660a984015260ab830137018a60ab82015203608b8101865260ab01610ab79086611d57565b808952886020528589209261012401610acf90611f53565b8651978896879586957f65a686de000000000000000000000000000000000000000000000000000000008752860194610b0795611f97565b039134907f0000000000000000000000000000000000000000000000000000000000000000165af18015610b6757610b3d578280f35b81610b5c92903d10610b60575b610b548183611d57565b810190611f68565b8280f35b503d610b4a565b505051903d90823e3d90fd5b878d517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b868c517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b610c1b8d610bd18a93611ea7565b90519384937f6de7824600000000000000000000000000000000000000000000000000000000855284019092916bffffffffffffffffffffffff6020916040840195845216910152565b0390fd5b60248f60118b7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b610c1b8d610c598a93611e94565b90517f6de7824600000000000000000000000000000000000000000000000000000000815265ffffffffffff94851693810193845293166020830152829160400190565b87610c1b8e610c598f611e94565b8780fd5b9050346103725760806003193601126103725780359267ffffffffffffffff9160443583811161037257610ce69036908301611a86565b95909360643590811161076857610d009036908401611a86565b92909473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163303610e3457610d49886120ee565b610d5588519182611d57565b888152602098898201923682820111610cab578188928c92863783010152519020828552848852610d90610d97888720895192838092611c44565b0382611d57565b88815191012003610e0e577fff000000000000000000000000000000000000000000000000000000000000008761032288610dff60218a8a8a80610ddc81858e612b7b565b87519a16898b015289938585013782019083820152036001810187520185611d57565b51928284938452830190611bae565b85517e3923e0000000000000000000000000000000000000000000000000000000008152fd5b5085517f48f5c3ed000000000000000000000000000000000000000000000000000000008152fd5b90503461037257816003193601126103725735906024359165ffffffffffff8316809303610763577f0f610a3dcd6165f6c2336e534655b0895a80b36322401c17b5bd0eea058ed1b992610eae612a94565b8185526001602052828520817fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000082541617905582519182526020820152a180f35b5050346101a15760209061039b610f0536611af4565b96959095949194939293612acc565b611ab4565b8383346101a15760606003193601126101a1578235906024359367ffffffffffffffff9485811161052657610f519036908301611a86565b91909360443587811161051857610f6b9036908401611a86565b939095610f76612a94565b6041820361127557828852602091888352610f93878a2054611bf1565b61124d5783895288835286892099811161122157610fb18a54611bf1565b99601f8b116111de575b89809b50979899601f83116001146111125760609383611058937f4bdc885fecec9b48853a8df98337140d59c2ebd57b0386bd50133873b969a331979561104a948d91611107575b508260011b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8460031b1c19161790555b8b5195869589875286015260608501916120af565b8281038a840152878b6120af565b0390a173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001691823b156105265784906110de8751988996879586947fffbeacc700000000000000000000000000000000000000000000000000000000865285015289602485015260448401916120af565b03925af19081156110fe57506110f2575080f35b6110fb90611cf8565b80f35b513d84823e3d90fd5b905083013538611003565b818952848920907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084168a5b8181106111c45750936110589361104a9360609793837f4bdc885fecec9b48853a8df98337140d59c2ebd57b0386bd50133873b969a3319a981061118c575b5050600182811b019055611035565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88560031b161c19908501351690558f8061117d565b8287013584558e9b5060019093019291870191870161113e565b808a52838a20601f830160051c81019b858410611217575b601f0160051c019a5b8b811061120c5750610fbb565b8a81556001016111ff565b909b508b906111f6565b6024896041877f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b8487517fb8e35614000000000000000000000000000000000000000000000000000000008152fd5b8386517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b5050346101a157816003193601126101a1576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b5050346101a157816003193601126101a1576020906002549051908152f35b8383346101a157816003193601126101a15780805180947f92006e730000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9283156113fe578280946113bb575b50505173ffffffffffffffffffffffffffffffffffffffff9190911681526020810191909152604090f35b91935091508282813d83116113f7575b6113d58183611d57565b810103126102e5575061032260206113ec83612075565b920151929084611390565b503d6113cb565b5051903d90823e3d90fd5b5050346101a157816003193601126101a15760209073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754915191168152f35b5050346101a157816003193601126101a1576020905160238152f35b83806003193601126102e55761148d612a94565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a35580f35b83806003193601126102e55763389a75e1600c52338152806020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c928280a280f35b919050346103725760206003193601126103725781359161153f612a94565b61012c8311156115ac576206270083116115855750816020917f88e95e1ea5dc9ffb42b44903f6dc9ba92e46f0ae1931bd6e6cbeecdf3d6b9a029360025551908152a180f35b90517f3f6368aa000000000000000000000000000000000000000000000000000000008152fd5b90517f6229dcd0000000000000000000000000000000000000000000000000000000008152fd5b905034610372576020600319360112610372578160209365ffffffffffff92358152600185522054169051908152f35b905060031960c0813601126107685781359167ffffffffffffffff8084116119775761014084830193853603011261197757366084116119775760a435908111611977576116549036908301611a86565b926064850190803594858952600160205265ffffffffffff80898b205416808261167d87611e94565b16106119695750898052600160205280898b205416908160848a01916116a283611e94565b161061195b57505060e487016116b781611ea7565b600b3a023a8104600b143a15171561192f57600a900491906bffffffffffffffffffffffff168211611921575050604487019360416116f68685611f02565b9050036118f95760248801604161170d8286611f02565b9050036118d157838361183e93611887979561173f8f8f611735819f9e9d9b9860e899611f02565b9b8c949198611f02565b938492519c8d997f010000000000000000000000000000000000000000000000000000000000000060208c01527f140000000000000000000000000000000000000000000000000000000000000060218c01528560228c01523360428c015260628b0137880192606284019081523701916024356062840152604435608284015260643560a284015260843560c28401527fffffffff000000000000000000000000000000000000000000000000000000004360e01b1660e28401527fffff0000000000000000000000000000000000000000000000000000000000008260f01b1660e684015285830137018c838201520360c8810185520183611d57565b84895288602052611855610124878b209801611f53565b91865197889687967f65a686de0000000000000000000000000000000000000000000000000000000088528701611f97565b03813473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af18015610b6757610b3d578280f35b868a517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b8589517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b610c1b8a610bd18993611ea7565b60248c60118a7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b610c1b8a610c598993611e94565b86610c1b8b610c5988611e94565b8580fd5b83806003193601126102e55763389a75e1600c523381526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d8280a280f35b5050346101a157816003193601126101a1576020905161015e8152f35b8490346101a15760606003193601126101a15760443567ffffffffffffffff811161037257611a159036908501611a86565b9173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163303611a5f57506110fb929335611d98565b807f48f5c3ed00000000000000000000000000000000000000000000000000000000869252fd5b9181601f840112156107635782359167ffffffffffffffff8311610763576020838186019501011161076357565b346107635760006003193601126107635760206040516103e88152f35b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361076357565b9060e06003198301126107635773ffffffffffffffffffffffffffffffffffffffff91600435838116810361076357926024358181168103610763579260443592606435926084359081168103610763579160a43561ffff81168103610763579160c4359067ffffffffffffffff821161076357611b7491600401611a86565b9091565b359065ffffffffffff8216820361076357565b60005b838110611b9e5750506000910152565b8181015183820152602001611b8e565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093611bea81518092818752878088019101611b8b565b0116010190565b90600182811c92168015611c3a575b6020831014611c0b57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691611c00565b805460009392611c5382611bf1565b91828252602093600191600181169081600014611cbb5750600114611c7a575b5050505050565b90939495506000929192528360002092846000945b838610611ca757505050500101903880808080611c73565b805485870183015294019385908201611c8f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168685015250505090151560051b010191503880808080611c73565b67ffffffffffffffff8111611d0c57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff821117611d0c57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117611d0c57604052565b8215611e65577fff0000000000000000000000000000000000000000000000000000000000000082351680611e025750826001116107635760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611e0094019201906128db565b565b60207fe3d0bbb47fe45a6223645bd4352bb4d9262d1acaa2c0ffd75fa050fca0439b7f91604051908152a1826001116107635760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611e009401920190612708565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b3565ffffffffffff811681036107635790565b356bffffffffffffffffffffffff811681036107635790565b81810292918115918404141715611ed357565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610763570180359067ffffffffffffffff82116107635760200191813603831361076357565b3567ffffffffffffffff811681036107635790565b9190826040910312610763576020825192015190565b35906bffffffffffffffffffffffff8216820361076357565b9395949290611fc692611fb891610140908752806020880152860190611c44565b908482036040860152611bae565b9365ffffffffffff80611fd883611b78565b166060850152611fea60208301611b78565b16608084015260408101359073ffffffffffffffffffffffffffffffffffffffff82168092036107635760a0918285015261202760608201611f7e565b6bffffffffffffffffffffffff80911660c086015261204860808301611f7e565b1660e085015201359067ffffffffffffffff90818316809303610763576101209261010085015216910152565b519073ffffffffffffffffffffffffffffffffffffffff8216820361076357565b9060416080928183526020830137600060618201520190565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b67ffffffffffffffff8111611d0c57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b91908201809211611ed357565b909392979695949561214d818789878d888b89612acc565b98896000526003602052604060002097600189015460a01c80612535575b506201000091808060101b04831481151715611ed3578261ffff8316810311611ed35761ffff8216831461250657604051927f119f8e680000000000000000000000000000000000000000000000000000000084528c600485015273ffffffffffffffffffffffffffffffffffffffff8916602485015286604485015261ffff831690039060101b046064830152602082608481600073ffffffffffffffffffffffffffffffffffffffff89165af191821561248e576000926124d2575b50600189015473ffffffffffffffffffffffffffffffffffffffff81166124a85761229e9060016bffffffffffffffffffffffff61226960025443612128565b868e551660a081901b7fffffffffffffffffffffffff000000000000000000000000000000000000000016331791909c015550565b61040b808302908382041483151715611ed3576103e86040519104606052306040523360601b602c526f23b872dd000000000000000000000000600c52602060006064601c828c5af13d1560016000511417161561249a57600060605260405261230c9061ffff1682611ec0565b60101c90039561231d878688612c5a565b8160021161076357803560f01c9182612398575b50505073ffffffffffffffffffffffffffffffffffffffff93848092604051988952166020880152166040860152606085015216608083015260a0820152827fb2b54dbcb6f6e94446d5dc07448faa5aff2a1fa2ef8280125cbc19a9b9514c4360c03393a3565b806016979394971161076357600282013560601c9360020161ffff8111611ed35761ffff16908160161161076357811161076357833b156107635761244593600092838a604051978895869485937f5ea79f2600000000000000000000000000000000000000000000000000000000855260048501526060602485015260167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea60648601930191016120af565b6001604483015203925af1801561248e5773ffffffffffffffffffffffffffffffffffffffff958693849261247f575b5092829650612331565b61248890611cf8565b38612475565b6040513d6000823e3d90fd5b637939f4246000526004601cfd5b60046040517fd0c27c9f000000000000000000000000000000000000000000000000000000008152fd5b9091506020813d6020116124fe575b816124ee60209383611d57565b8101031261076357519038612229565b3d91506124e1565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6bfffffffffffffffffffffffb8110806125f3575b156125b5576bffffffffffffffffffffffff80431691828260048301161015612578575b5050505b3861216b565b6003190116111561258b5738808061256e565b60046040517f695b3a94000000000000000000000000000000000000000000000000000000008152fd5b6bffffffffffffffffffffffff4316036125725760046040517f695b3a94000000000000000000000000000000000000000000000000000000008152fd5b506004811161254a565b73ffffffffffffffffffffffffffffffffffffffff604051917f1400000000000000000000000000000000000000000000000000000000000000602084015260006021840152166041820152604181526080810181811067ffffffffffffffff821117611d0c5760405290565b9460a094604163ffffffff9599989473ffffffffffffffffffffffffffffffffffffffff94895260c060208a01528160c08a015260e089013760006101218801526101408701986040880152606087015216608085015216910152565b93604163ffffffff93608095989793875260a060208801528160a088015260c087013760006101018601526101208501966040860152606085015216910152565b90918015611e65577fff00000000000000000000000000000000000000000000000000000000000000833516928160421161076357602e81013560601c8415612842577f010000000000000000000000000000000000000000000000000000000000000085146127a357602485604051907f9f7697910000000000000000000000000000000000000000000000000000000082526004820152fd5b90918093945060c411610763578260e4116107635761014492808411610763576101481161076357803b15610763576128289360008094604051968795869485937f16604f4f00000000000000000000000000000000000000000000000000000000855282013560e01c9161012481013591608360c4830135920190600487016126c7565b03925af1801561248e576128395750565b611e0090611cf8565b9093508160c493929311610763578260e41161076357826101251161076357610166928084116107635761016a1161076357803b15610763576128289360008094604051968795869485937f1fd1602b00000000000000000000000000000000000000000000000000000000855281013560e01c9161015282013560601c9161010581013591608360c48301359201906004880161266a565b90918015611e65577fff00000000000000000000000000000000000000000000000000000000000000833516928160421161076357602e81013560601c84156129fb577f0100000000000000000000000000000000000000000000000000000000000000851461297657602485604051907f9f7697910000000000000000000000000000000000000000000000000000000082526004820152fd5b90918093945060c411610763578260e4116107635761014492808411610763576101481161076357803b15610763576128289360008094604051968795869485937feb80cd1100000000000000000000000000000000000000000000000000000000855282013560e01c9161012481013591608360c4830135920190600487016126c7565b9093508160c493929311610763578260e41161076357826101251161076357610166928084116107635761016a1161076357803b15610763576128289360008094604051968795869485937ff754fc3500000000000000000000000000000000000000000000000000000000855281013560e01c9161015282013560601c9161010581013591608360c48301359201906004880161266a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927543303612abe57565b6382b429006000526004601cfd5b969391849396929591957fffffffffffffffffffffffffffffffffffffffff000000000000000000000000978892604051998a998160208c019d60601b168d5260601b1660348a01526048890152606888015260601b16608886015260f01b7fffff00000000000000000000000000000000000000000000000000000000000016609c850152609e8401378101609e81016000905203607e81018252609e01612b759082611d57565b51902090565b91908115611e65577fff00000000000000000000000000000000000000000000000000000000000000813516928260831015611e65576014608383013560f81c03612c30578260a411610763576084820135612c30578260b0116107635760a482013560a01c612c30578315612c26577f0100000000000000000000000000000000000000000000000000000000000000809414612c195750505090565b612c239350612e14565b90565b612c239350612ca3565b60046040517fe6c4247b000000000000000000000000000000000000000000000000000000008152fd5b60109260209260145260345260446000938480936fa9059cbb00000000000000000000000082525af13d156001835114171615612c9657603452565b6390b8ec1890526004601cfd5b919081604211610763578160831161076357606f81013560601c61016c838111610763578360e41015611e6557604051907f904e0a0100000000000000000000000000000000000000000000000000000000825260e484013560f81c6004830152602082602481865afa91821561248e57600092612da8575b508460c411610763578460e4116107635784610105116107635785612d929361016a86013560f01c93612d8d60018801947ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe948a019089018760b08b013560601c60e58c013560c48d01358789612acc565b613074565b15612d9f57505050600090565b612c239261323c565b9091506020813d602011612ddc575b81612dc460209383611d57565b8101031261076357612dd590612075565b9038612d1c565b3d9150612db7565b3d15612e0f573d90612df5826120ee565b91612e036040519384611d57565b82523d6000602084013e565b606090565b90600092806042116107685780608311610768578060c411610768578060e41161076857610104908082116105265761012481811161197757610144948286116105185761014893838511610cab57906020929160405197889384937f34420dbd00000000000000000000000000000000000000000000000000000000855260048501526101006024850152612eaf83850160018b01612096565b9260b08a013560601c604486015260c48a0135606486015260e48a0135608486015289013560a485015288013560c484015287013560e01c60e4830152038188606f88013560601c5af1859481613040575b50612f1b575050505050612c23612f16612de4565b6134d0565b61014a918183116119775783013560f01c9384612f3b575b505050505090565b61015e91808311610518578385013560601c9386810180911161301357808411610cab571161197757823b1561197757908580949392612fdb604051978896879586947f5ea79f260000000000000000000000000000000000000000000000000000000086526004860152606060248601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec60648601930191016120af565b82604483015203925af1801561300857612ff9575b80808080612f33565b61300290611cf8565b38612ff0565b6040513d84823e3d90fd5b6024887f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b9094506020813d60201161306c575b8161305c60209383611d57565b8101031261197757519338612f01565b3d915061304f565b94909293919360009486865260036020526040862093845492600186019273ffffffffffffffffffffffffffffffffffffffff938481541697881561322d578a905573ffffffffffffffffffffffffffffffffffffffff8a164360a01b7fffffffffffffffffffffffff000000000000000000000000000000000000000016179055831692833b156107495786898995604182968e96610144966040519a8b998a987fc86380d9000000000000000000000000000000000000000000000000000000008a5260048a015260248901528c6044890152166064870152608486015260c060a48601528160c486015260e4850137816101258401525af180156132225761320f575b506023810291818304602314821517156131e2577f8ff233d871d7b0eb7094811a82d2a3abe0a4a3ff0066b7f402fbdd5b88f023789594926103e86131cb6131db969461ffff6131d5951690611ec0565b60101c9104612128565b91612c5a565b80a2600190565b6024867f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b61321b90959195611cf8565b933861317a565b6040513d88823e3d90fd5b50505050505050505050905090565b90600092806042116107685780608311610768578060e41015613459578060c411610768578060e4116107685761010592818411610526576101259082821161197757610166838111610518578361016a1161051857906020929161332d60405197889485947f13193e6a000000000000000000000000000000000000000000000000000000008652600486015261012060248601526132e3610124860160018b01612096565b9160e48a013560f81c604487015260b08a013560601c606487015260c48a0135608487015260e58a013560a487015289013560c48601526003198583030160e48601528801612096565b9086013560e01c610104830152038187606f87013560601c5af1849381613425575b506133635750505050612c23612f16612de4565b61016e908082116105265761016c83013560f01c938461338557505050505090565b61018291808311610518578385013560601c9386810180911161301357808411610cab571161197757823b1561197757908580949392612fdb604051978896879586947f5ea79f260000000000000000000000000000000000000000000000000000000086526004860152606060248601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec60648601930191016120af565b9093506020813d602011613451575b8161344160209383611d57565b810103126105265751923861334f565b3d9150613434565b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b90602082519201517fffffffffffffffff0000000000000000000000000000000000000000000000009081811693600881106134c157505050565b60080360031b82901b16169150565b6134d990613486565b6135176040517f7c1e66d00000000000000000000000000000000000000000000000000000000060208201526004815261351281611d3b565b613486565b7fffffffffffffffff0000000000000000000000000000000000000000000000009182169190811682146136335781816135806040517f24557f050000000000000000000000000000000000000000000000000000000060208201526004815261351281611d3b565b161461360d576135bf6040517f2c64c1b20000000000000000000000000000000000000000000000000000000060208201526004815261351281611d3b565b16146135e9577f100000000000000000000000000000000000000000000000000000000000000090565b7f130000000000000000000000000000000000000000000000000000000000000090565b50507f120000000000000000000000000000000000000000000000000000000000000090565b50507f11000000000000000000000000000000000000000000000000000000000000009056fea2646970667358221220808e4fc618233f231535d012f6c96d52b17d5442bd4565852c1ba551e5a0ba0664736f6c634300081600330000000000000000000000003c5c5436bca59042cbc835276e51428781366d85000000000000000000000000ca7ca70655639717e97c2d0ed817a5369cca8cce
Deployed Bytecode
0x60a060408181526004918236101561001657600080fd5b600092833560e01c91826311cbf805146119e3575081631e2b8990146119c6578163256929621461197b578163318987c4146116035781634f37a457146115d35781635330a5de1461152057816354d1f13d146114da578163715018a614611479578163898e68ad1461145d5781638da5cb5b1461140957816392006e731461130d57816394aaf73f146112ee578163989e34b31461129d578163ac23728114610f14578163b40f969a14610f19578163ba3c99e614610f14578163bf09469414610eef578163ca9b7b2f14610e5c578163d00689b614610caf578163d6512967146107de578163d94501871461078b578163daf1e1671461076c578163dbaed76b146105d7578163dd4aeedd146103a2578163e5a7783514610376578163e73ae76414610326578163ea901f1a146102e8578163f04e283e1461023c578163f2fde38b146101a5575063fee81cf41461016f57600080fd5b346101a15760206003193601126101a15760209161018b611ad1565b9063389a75e1600c525281600c20549051908152f35b5080fd5b839060206003193601126101a1576101bb611ad1565b906101c4612a94565b8160601b15610231575073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a35580f35b637448fbae8352601cfd5b8360206003193601126102e557610251611ad1565b610259612a94565b63389a75e1600c528082526020600c2092835442116102da57508173ffffffffffffffffffffffffffffffffffffffff929355167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a35580f35b636f5e88188352601cfd5b80fd5b5050346101a15760206003193601126101a1576103229061030f61030a611ad1565b6125fd565b9051918291602083526020830190611bae565b0390f35b90503461037257602060031936011261037257906103608161032294610359943581528060205220825193848092611c44565b0383611d57565b51918291602083526020830190611bae565b8280fd5b5050346101a15760209061039b61038c36611af4565b96959095949194939293612135565b9051908152f35b91905034610372576103c9906103b736611af4565b928699948684979c988a959495612acc565b9586885260036020528488209260018401549373ffffffffffffffffffffffffffffffffffffffff908186169081156105a857908b94939291549660a01c90330361052a575b508984526003602052600060018986208281550155808716803b15610526578492836084928d948c5197889687957fb9ff629b00000000000000000000000000000000000000000000000000000000875286015260248501528a60448501528d1660648401525af1801561051c576104fc575b50507f5d002c935f79d7b578b7c23b1101d4508bb3bd36e5b53c2aa00263ee5ac9f0de9461ffff936104f695936104e09360236103e897889216830260101c920204019461015e86020480956104d9823386612c5a565b0391612c5a565b5133815260208101919091529081906040820190565b0390a280f35b6105099095939495611cf8565b61051857863894939294610482565b8680fd5b86513d84823e3d90fd5b8480fd5b909192935043811161054057908a93929161040f565b82889143810390811161057a5760249251917f62141db5000000000000000000000000000000000000000000000000000000008352820152fd5b6011827f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b6024848c8b51917fae029d69000000000000000000000000000000000000000000000000000000008352820152fd5b8383346101a1576101206003193601126101a15767ffffffffffffffff6024358181116107685761060b9036908601611a86565b91906044359073ffffffffffffffffffffffffffffffffffffffff9182811695868203610763576064359284841684036107635760c43594851685036107635760e4359561ffff87168703610763576101043590811161075f576106be916106788c602093369101611a86565b9a90998d8651958694859384937f66ae79e600000000000000000000000000000000000000000000000000000000855280359085015289602485015260448401916120af565b03915afa908115610755578991610716575b50156106ee57506106ea96975060a4359160843591612135565b5080f35b8890517fea66ca6d000000000000000000000000000000000000000000000000000000008152fd5b90506020813d60201161074d575b8161073160209383611d57565b8101031261074957518015158103610749578a6106d0565b8880fd5b3d9150610724565b82513d8b823e3d90fd5b8980fd5b600080fd5b8380fd5b5050346101a157816003193601126101a1576103229061030f306125fd565b9050346103725760206003193601126103725760609282913581526003602052209060018254920154815192835273ffffffffffffffffffffffffffffffffffffffff8116602084015260a01c90820152f35b6003198484610100368401126101a15783359367ffffffffffffffff80861161076857610140868301958736030112610768576024359560ff871687036105265760a4359273ffffffffffffffffffffffffffffffffffffffff928385168095036107635760c4359061ffff821682036107635760e435908111610cab57908899610870899a98999336908601611a86565b9290918a61089d60648901809c3597888152600160205265ffffffffffff80948192205416928392611e94565b1610610c9d5750808c8e8080526001602052205416908160848a01916108c283611e94565b1610610c4b57505060e487016108d781611ea7565b600b3a023a8104600b143a151715610c1f57600a900491906bffffffffffffffffffffffff168211610bc3575050908c9291604161091960448a018096611f02565b905003610b9b578d604161093160248b018093611f02565b905003610b7357918e8e9f928e9f979695939e809e9f889661095291611f02565b9761095d9194611f02565b90818984519a8b976020890160805260805137860191602083018d8152370191604435602084015260f81b7fff00000000000000000000000000000000000000000000000000000000000000169082015260643560418201526084356061820152036061810185526081016109d29085611d57565b7fffff00000000000000000000000000000000000000000000000000000000000080918d519c888e9960208b01527f1400000000000000000000000000000000000000000000000000000000000000978860218c01528160228c01523360428c01525160628b018160805191610a4792611b8b565b8a01976062890152606388015260838701524360e01b7fffffffff000000000000000000000000000000000000000000000000000000001660a387015260f01b1660a78501528260f01b1660a984015260ab830137018a60ab82015203608b8101865260ab01610ab79086611d57565b808952886020528589209261012401610acf90611f53565b8651978896879586957f65a686de000000000000000000000000000000000000000000000000000000008752860194610b0795611f97565b039134907f0000000000000000000000003c5c5436bca59042cbc835276e51428781366d85165af18015610b6757610b3d578280f35b81610b5c92903d10610b60575b610b548183611d57565b810190611f68565b8280f35b503d610b4a565b505051903d90823e3d90fd5b878d517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b868c517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b610c1b8d610bd18a93611ea7565b90519384937f6de7824600000000000000000000000000000000000000000000000000000000855284019092916bffffffffffffffffffffffff6020916040840195845216910152565b0390fd5b60248f60118b7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b610c1b8d610c598a93611e94565b90517f6de7824600000000000000000000000000000000000000000000000000000000815265ffffffffffff94851693810193845293166020830152829160400190565b87610c1b8e610c598f611e94565b8780fd5b9050346103725760806003193601126103725780359267ffffffffffffffff9160443583811161037257610ce69036908301611a86565b95909360643590811161076857610d009036908401611a86565b92909473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003c5c5436bca59042cbc835276e51428781366d85163303610e3457610d49886120ee565b610d5588519182611d57565b888152602098898201923682820111610cab578188928c92863783010152519020828552848852610d90610d97888720895192838092611c44565b0382611d57565b88815191012003610e0e577fff000000000000000000000000000000000000000000000000000000000000008761032288610dff60218a8a8a80610ddc81858e612b7b565b87519a16898b015289938585013782019083820152036001810187520185611d57565b51928284938452830190611bae565b85517e3923e0000000000000000000000000000000000000000000000000000000008152fd5b5085517f48f5c3ed000000000000000000000000000000000000000000000000000000008152fd5b90503461037257816003193601126103725735906024359165ffffffffffff8316809303610763577f0f610a3dcd6165f6c2336e534655b0895a80b36322401c17b5bd0eea058ed1b992610eae612a94565b8185526001602052828520817fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000082541617905582519182526020820152a180f35b5050346101a15760209061039b610f0536611af4565b96959095949194939293612acc565b611ab4565b8383346101a15760606003193601126101a1578235906024359367ffffffffffffffff9485811161052657610f519036908301611a86565b91909360443587811161051857610f6b9036908401611a86565b939095610f76612a94565b6041820361127557828852602091888352610f93878a2054611bf1565b61124d5783895288835286892099811161122157610fb18a54611bf1565b99601f8b116111de575b89809b50979899601f83116001146111125760609383611058937f4bdc885fecec9b48853a8df98337140d59c2ebd57b0386bd50133873b969a331979561104a948d91611107575b508260011b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8460031b1c19161790555b8b5195869589875286015260608501916120af565b8281038a840152878b6120af565b0390a173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003c5c5436bca59042cbc835276e51428781366d851691823b156105265784906110de8751988996879586947fffbeacc700000000000000000000000000000000000000000000000000000000865285015289602485015260448401916120af565b03925af19081156110fe57506110f2575080f35b6110fb90611cf8565b80f35b513d84823e3d90fd5b905083013538611003565b818952848920907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084168a5b8181106111c45750936110589361104a9360609793837f4bdc885fecec9b48853a8df98337140d59c2ebd57b0386bd50133873b969a3319a981061118c575b5050600182811b019055611035565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88560031b161c19908501351690558f8061117d565b8287013584558e9b5060019093019291870191870161113e565b808a52838a20601f830160051c81019b858410611217575b601f0160051c019a5b8b811061120c5750610fbb565b8a81556001016111ff565b909b508b906111f6565b6024896041877f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b8487517fb8e35614000000000000000000000000000000000000000000000000000000008152fd5b8386517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b5050346101a157816003193601126101a1576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003c5c5436bca59042cbc835276e51428781366d85168152f35b5050346101a157816003193601126101a1576020906002549051908152f35b8383346101a157816003193601126101a15780805180947f92006e730000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003c5c5436bca59042cbc835276e51428781366d85165afa9283156113fe578280946113bb575b50505173ffffffffffffffffffffffffffffffffffffffff9190911681526020810191909152604090f35b91935091508282813d83116113f7575b6113d58183611d57565b810103126102e5575061032260206113ec83612075565b920151929084611390565b503d6113cb565b5051903d90823e3d90fd5b5050346101a157816003193601126101a15760209073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754915191168152f35b5050346101a157816003193601126101a1576020905160238152f35b83806003193601126102e55761148d612a94565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a35580f35b83806003193601126102e55763389a75e1600c52338152806020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c928280a280f35b919050346103725760206003193601126103725781359161153f612a94565b61012c8311156115ac576206270083116115855750816020917f88e95e1ea5dc9ffb42b44903f6dc9ba92e46f0ae1931bd6e6cbeecdf3d6b9a029360025551908152a180f35b90517f3f6368aa000000000000000000000000000000000000000000000000000000008152fd5b90517f6229dcd0000000000000000000000000000000000000000000000000000000008152fd5b905034610372576020600319360112610372578160209365ffffffffffff92358152600185522054169051908152f35b905060031960c0813601126107685781359167ffffffffffffffff8084116119775761014084830193853603011261197757366084116119775760a435908111611977576116549036908301611a86565b926064850190803594858952600160205265ffffffffffff80898b205416808261167d87611e94565b16106119695750898052600160205280898b205416908160848a01916116a283611e94565b161061195b57505060e487016116b781611ea7565b600b3a023a8104600b143a15171561192f57600a900491906bffffffffffffffffffffffff168211611921575050604487019360416116f68685611f02565b9050036118f95760248801604161170d8286611f02565b9050036118d157838361183e93611887979561173f8f8f611735819f9e9d9b9860e899611f02565b9b8c949198611f02565b938492519c8d997f010000000000000000000000000000000000000000000000000000000000000060208c01527f140000000000000000000000000000000000000000000000000000000000000060218c01528560228c01523360428c015260628b0137880192606284019081523701916024356062840152604435608284015260643560a284015260843560c28401527fffffffff000000000000000000000000000000000000000000000000000000004360e01b1660e28401527fffff0000000000000000000000000000000000000000000000000000000000008260f01b1660e684015285830137018c838201520360c8810185520183611d57565b84895288602052611855610124878b209801611f53565b91865197889687967f65a686de0000000000000000000000000000000000000000000000000000000088528701611f97565b03813473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003c5c5436bca59042cbc835276e51428781366d85165af18015610b6757610b3d578280f35b868a517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b8589517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b610c1b8a610bd18993611ea7565b60248c60118a7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b610c1b8a610c598993611e94565b86610c1b8b610c5988611e94565b8580fd5b83806003193601126102e55763389a75e1600c523381526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d8280a280f35b5050346101a157816003193601126101a1576020905161015e8152f35b8490346101a15760606003193601126101a15760443567ffffffffffffffff811161037257611a159036908501611a86565b9173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003c5c5436bca59042cbc835276e51428781366d85163303611a5f57506110fb929335611d98565b807f48f5c3ed00000000000000000000000000000000000000000000000000000000869252fd5b9181601f840112156107635782359167ffffffffffffffff8311610763576020838186019501011161076357565b346107635760006003193601126107635760206040516103e88152f35b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361076357565b9060e06003198301126107635773ffffffffffffffffffffffffffffffffffffffff91600435838116810361076357926024358181168103610763579260443592606435926084359081168103610763579160a43561ffff81168103610763579160c4359067ffffffffffffffff821161076357611b7491600401611a86565b9091565b359065ffffffffffff8216820361076357565b60005b838110611b9e5750506000910152565b8181015183820152602001611b8e565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093611bea81518092818752878088019101611b8b565b0116010190565b90600182811c92168015611c3a575b6020831014611c0b57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691611c00565b805460009392611c5382611bf1565b91828252602093600191600181169081600014611cbb5750600114611c7a575b5050505050565b90939495506000929192528360002092846000945b838610611ca757505050500101903880808080611c73565b805485870183015294019385908201611c8f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168685015250505090151560051b010191503880808080611c73565b67ffffffffffffffff8111611d0c57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff821117611d0c57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117611d0c57604052565b8215611e65577fff0000000000000000000000000000000000000000000000000000000000000082351680611e025750826001116107635760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611e0094019201906128db565b565b60207fe3d0bbb47fe45a6223645bd4352bb4d9262d1acaa2c0ffd75fa050fca0439b7f91604051908152a1826001116107635760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611e009401920190612708565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b3565ffffffffffff811681036107635790565b356bffffffffffffffffffffffff811681036107635790565b81810292918115918404141715611ed357565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610763570180359067ffffffffffffffff82116107635760200191813603831361076357565b3567ffffffffffffffff811681036107635790565b9190826040910312610763576020825192015190565b35906bffffffffffffffffffffffff8216820361076357565b9395949290611fc692611fb891610140908752806020880152860190611c44565b908482036040860152611bae565b9365ffffffffffff80611fd883611b78565b166060850152611fea60208301611b78565b16608084015260408101359073ffffffffffffffffffffffffffffffffffffffff82168092036107635760a0918285015261202760608201611f7e565b6bffffffffffffffffffffffff80911660c086015261204860808301611f7e565b1660e085015201359067ffffffffffffffff90818316809303610763576101209261010085015216910152565b519073ffffffffffffffffffffffffffffffffffffffff8216820361076357565b9060416080928183526020830137600060618201520190565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b67ffffffffffffffff8111611d0c57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b91908201809211611ed357565b909392979695949561214d818789878d888b89612acc565b98896000526003602052604060002097600189015460a01c80612535575b506201000091808060101b04831481151715611ed3578261ffff8316810311611ed35761ffff8216831461250657604051927f119f8e680000000000000000000000000000000000000000000000000000000084528c600485015273ffffffffffffffffffffffffffffffffffffffff8916602485015286604485015261ffff831690039060101b046064830152602082608481600073ffffffffffffffffffffffffffffffffffffffff89165af191821561248e576000926124d2575b50600189015473ffffffffffffffffffffffffffffffffffffffff81166124a85761229e9060016bffffffffffffffffffffffff61226960025443612128565b868e551660a081901b7fffffffffffffffffffffffff000000000000000000000000000000000000000016331791909c015550565b61040b808302908382041483151715611ed3576103e86040519104606052306040523360601b602c526f23b872dd000000000000000000000000600c52602060006064601c828c5af13d1560016000511417161561249a57600060605260405261230c9061ffff1682611ec0565b60101c90039561231d878688612c5a565b8160021161076357803560f01c9182612398575b50505073ffffffffffffffffffffffffffffffffffffffff93848092604051988952166020880152166040860152606085015216608083015260a0820152827fb2b54dbcb6f6e94446d5dc07448faa5aff2a1fa2ef8280125cbc19a9b9514c4360c03393a3565b806016979394971161076357600282013560601c9360020161ffff8111611ed35761ffff16908160161161076357811161076357833b156107635761244593600092838a604051978895869485937f5ea79f2600000000000000000000000000000000000000000000000000000000855260048501526060602485015260167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea60648601930191016120af565b6001604483015203925af1801561248e5773ffffffffffffffffffffffffffffffffffffffff958693849261247f575b5092829650612331565b61248890611cf8565b38612475565b6040513d6000823e3d90fd5b637939f4246000526004601cfd5b60046040517fd0c27c9f000000000000000000000000000000000000000000000000000000008152fd5b9091506020813d6020116124fe575b816124ee60209383611d57565b8101031261076357519038612229565b3d91506124e1565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6bfffffffffffffffffffffffb8110806125f3575b156125b5576bffffffffffffffffffffffff80431691828260048301161015612578575b5050505b3861216b565b6003190116111561258b5738808061256e565b60046040517f695b3a94000000000000000000000000000000000000000000000000000000008152fd5b6bffffffffffffffffffffffff4316036125725760046040517f695b3a94000000000000000000000000000000000000000000000000000000008152fd5b506004811161254a565b73ffffffffffffffffffffffffffffffffffffffff604051917f1400000000000000000000000000000000000000000000000000000000000000602084015260006021840152166041820152604181526080810181811067ffffffffffffffff821117611d0c5760405290565b9460a094604163ffffffff9599989473ffffffffffffffffffffffffffffffffffffffff94895260c060208a01528160c08a015260e089013760006101218801526101408701986040880152606087015216608085015216910152565b93604163ffffffff93608095989793875260a060208801528160a088015260c087013760006101018601526101208501966040860152606085015216910152565b90918015611e65577fff00000000000000000000000000000000000000000000000000000000000000833516928160421161076357602e81013560601c8415612842577f010000000000000000000000000000000000000000000000000000000000000085146127a357602485604051907f9f7697910000000000000000000000000000000000000000000000000000000082526004820152fd5b90918093945060c411610763578260e4116107635761014492808411610763576101481161076357803b15610763576128289360008094604051968795869485937f16604f4f00000000000000000000000000000000000000000000000000000000855282013560e01c9161012481013591608360c4830135920190600487016126c7565b03925af1801561248e576128395750565b611e0090611cf8565b9093508160c493929311610763578260e41161076357826101251161076357610166928084116107635761016a1161076357803b15610763576128289360008094604051968795869485937f1fd1602b00000000000000000000000000000000000000000000000000000000855281013560e01c9161015282013560601c9161010581013591608360c48301359201906004880161266a565b90918015611e65577fff00000000000000000000000000000000000000000000000000000000000000833516928160421161076357602e81013560601c84156129fb577f0100000000000000000000000000000000000000000000000000000000000000851461297657602485604051907f9f7697910000000000000000000000000000000000000000000000000000000082526004820152fd5b90918093945060c411610763578260e4116107635761014492808411610763576101481161076357803b15610763576128289360008094604051968795869485937feb80cd1100000000000000000000000000000000000000000000000000000000855282013560e01c9161012481013591608360c4830135920190600487016126c7565b9093508160c493929311610763578260e41161076357826101251161076357610166928084116107635761016a1161076357803b15610763576128289360008094604051968795869485937ff754fc3500000000000000000000000000000000000000000000000000000000855281013560e01c9161015282013560601c9161010581013591608360c48301359201906004880161266a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927543303612abe57565b6382b429006000526004601cfd5b969391849396929591957fffffffffffffffffffffffffffffffffffffffff000000000000000000000000978892604051998a998160208c019d60601b168d5260601b1660348a01526048890152606888015260601b16608886015260f01b7fffff00000000000000000000000000000000000000000000000000000000000016609c850152609e8401378101609e81016000905203607e81018252609e01612b759082611d57565b51902090565b91908115611e65577fff00000000000000000000000000000000000000000000000000000000000000813516928260831015611e65576014608383013560f81c03612c30578260a411610763576084820135612c30578260b0116107635760a482013560a01c612c30578315612c26577f0100000000000000000000000000000000000000000000000000000000000000809414612c195750505090565b612c239350612e14565b90565b612c239350612ca3565b60046040517fe6c4247b000000000000000000000000000000000000000000000000000000008152fd5b60109260209260145260345260446000938480936fa9059cbb00000000000000000000000082525af13d156001835114171615612c9657603452565b6390b8ec1890526004601cfd5b919081604211610763578160831161076357606f81013560601c61016c838111610763578360e41015611e6557604051907f904e0a0100000000000000000000000000000000000000000000000000000000825260e484013560f81c6004830152602082602481865afa91821561248e57600092612da8575b508460c411610763578460e4116107635784610105116107635785612d929361016a86013560f01c93612d8d60018801947ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe948a019089018760b08b013560601c60e58c013560c48d01358789612acc565b613074565b15612d9f57505050600090565b612c239261323c565b9091506020813d602011612ddc575b81612dc460209383611d57565b8101031261076357612dd590612075565b9038612d1c565b3d9150612db7565b3d15612e0f573d90612df5826120ee565b91612e036040519384611d57565b82523d6000602084013e565b606090565b90600092806042116107685780608311610768578060c411610768578060e41161076857610104908082116105265761012481811161197757610144948286116105185761014893838511610cab57906020929160405197889384937f34420dbd00000000000000000000000000000000000000000000000000000000855260048501526101006024850152612eaf83850160018b01612096565b9260b08a013560601c604486015260c48a0135606486015260e48a0135608486015289013560a485015288013560c484015287013560e01c60e4830152038188606f88013560601c5af1859481613040575b50612f1b575050505050612c23612f16612de4565b6134d0565b61014a918183116119775783013560f01c9384612f3b575b505050505090565b61015e91808311610518578385013560601c9386810180911161301357808411610cab571161197757823b1561197757908580949392612fdb604051978896879586947f5ea79f260000000000000000000000000000000000000000000000000000000086526004860152606060248601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec60648601930191016120af565b82604483015203925af1801561300857612ff9575b80808080612f33565b61300290611cf8565b38612ff0565b6040513d84823e3d90fd5b6024887f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b9094506020813d60201161306c575b8161305c60209383611d57565b8101031261197757519338612f01565b3d915061304f565b94909293919360009486865260036020526040862093845492600186019273ffffffffffffffffffffffffffffffffffffffff938481541697881561322d578a905573ffffffffffffffffffffffffffffffffffffffff8a164360a01b7fffffffffffffffffffffffff000000000000000000000000000000000000000016179055831692833b156107495786898995604182968e96610144966040519a8b998a987fc86380d9000000000000000000000000000000000000000000000000000000008a5260048a015260248901528c6044890152166064870152608486015260c060a48601528160c486015260e4850137816101258401525af180156132225761320f575b506023810291818304602314821517156131e2577f8ff233d871d7b0eb7094811a82d2a3abe0a4a3ff0066b7f402fbdd5b88f023789594926103e86131cb6131db969461ffff6131d5951690611ec0565b60101c9104612128565b91612c5a565b80a2600190565b6024867f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b61321b90959195611cf8565b933861317a565b6040513d88823e3d90fd5b50505050505050505050905090565b90600092806042116107685780608311610768578060e41015613459578060c411610768578060e4116107685761010592818411610526576101259082821161197757610166838111610518578361016a1161051857906020929161332d60405197889485947f13193e6a000000000000000000000000000000000000000000000000000000008652600486015261012060248601526132e3610124860160018b01612096565b9160e48a013560f81c604487015260b08a013560601c606487015260c48a0135608487015260e58a013560a487015289013560c48601526003198583030160e48601528801612096565b9086013560e01c610104830152038187606f87013560601c5af1849381613425575b506133635750505050612c23612f16612de4565b61016e908082116105265761016c83013560f01c938461338557505050505090565b61018291808311610518578385013560601c9386810180911161301357808411610cab571161197757823b1561197757908580949392612fdb604051978896879586947f5ea79f260000000000000000000000000000000000000000000000000000000086526004860152606060248601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec60648601930191016120af565b9093506020813d602011613451575b8161344160209383611d57565b810103126105265751923861334f565b3d9150613434565b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b90602082519201517fffffffffffffffff0000000000000000000000000000000000000000000000009081811693600881106134c157505050565b60080360031b82901b16169150565b6134d990613486565b6135176040517f7c1e66d00000000000000000000000000000000000000000000000000000000060208201526004815261351281611d3b565b613486565b7fffffffffffffffff0000000000000000000000000000000000000000000000009182169190811682146136335781816135806040517f24557f050000000000000000000000000000000000000000000000000000000060208201526004815261351281611d3b565b161461360d576135bf6040517f2c64c1b20000000000000000000000000000000000000000000000000000000060208201526004815261351281611d3b565b16146135e9577f100000000000000000000000000000000000000000000000000000000000000090565b7f130000000000000000000000000000000000000000000000000000000000000090565b50507f120000000000000000000000000000000000000000000000000000000000000090565b50507f11000000000000000000000000000000000000000000000000000000000000009056fea2646970667358221220808e4fc618233f231535d012f6c96d52b17d5442bd4565852c1ba551e5a0ba0664736f6c63430008160033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000003c5c5436bca59042cbc835276e51428781366d85000000000000000000000000ca7ca70655639717e97c2d0ed817a5369cca8cce
-----Decoded View---------------
Arg [0] : GARP_ (address): 0x3C5C5436BCa59042cBC835276E51428781366d85
Arg [1] : defaultOwner (address): 0xCa7ca70655639717e97C2D0eD817A5369cCA8cCE
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000003c5c5436bca59042cbc835276e51428781366d85
Arg [1] : 000000000000000000000000ca7ca70655639717e97c2d0ed817a5369cca8cce
Deployed Bytecode Sourcemap
1949:52777:9:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;:::i;:::-;11885:237:6;;;;;;;;;1949:52777:9;;;;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;:::i;:::-;12478:70:6;;;:::i;:::-;8479:183;;;;;;6813:405;;;;;;;;1949:52777:9;6813:405:6;;;1949:52777:9;;8479:183:6;;;;;;1949:52777:9;;;-1:-1:-1;;1949:52777:9;;;;;;;:::i;:::-;12478:70:6;;:::i;:::-;10506:526;;;;;;1949:52777:9;10506:526:6;;;;;;;;;;;6813:405;10506:526;;;6813:405;;;;;;1949:52777:9;6813:405:6;;;1949:52777:9;;10506:526:6;;;;;;1949:52777:9;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;46832:193;1949:52777;;;;:::i;:::-;46832:193;;;;;;;;;;;;;;:::i;:::-;1949:52777;;;;47082:19;1949:52777;;;;;47238:24;;;;1949:52777;;;;;;;47276:27;;;47272:74;;1949:52777;;;;;;;;;;47803:10;;:27;47799:220;;1949:52777;;;;;47082:19;1949:52777;;;47238:24;1949:52777;;;;;;;;;;;48229:95;;;;;1949:52777;;;;;;;;;48229:95;;;;;1624:4:10;48229:95:9;;;;1949:52777;;;;;;;;;;;;;;;;48229:95;;;;;;;;1949:52777;6200:4;;49841:115;6200:4;1949:52777;6200:4;49841:115;6200:4;;49810:10;6200:4;6100:2;6200:4;1949:52777;;;;;;48685:2;1949:52777;;;;;;6397:3;1949:52777;;;47803:10;;49587:11;47803:10;;49587:11;;:::i;:::-;1949:52777;49810:10;;:::i;:::-;1949:52777;47803:10;1949:52777;;;;;;;;;;;;;;;;;49841:115;;;;1949:52777;;48229:95;;;;;;;;:::i;:::-;1949:52777;;48229:95;;;;;;;;1949:52777;;;;48229:95;1949:52777;;1624:4:10;1949:52777:9;;1624:4:10;;;;48229:95:9;1949:52777;;;47799:220;47940:12;;;;;;47915:37;;47911:97;;47799:220;;;;;;;47911:97;47940:12;;;;1949:52777;;;;;;;;;;47961:47;;;;;;1949:52777;47961:47;1949:52777;;;;;;;;;;47272:74;1949:52777;;;;;47312:34;;;;;;1949:52777;47312:34;1949:52777;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;36097:75;;;;;;;1624:4:10;36097:75:9;;1949:52777;;36097:75;;;1949:52777;;;;;;;;;;;:::i;:::-;36097:75;;;;;;;;;;;;;1949:52777;36096:76;;36092:108;;1949:52777;36211:179;1949:52777;;;;;;;;36211:179;;:::i;:::-;;1949:52777;;36092:108;1949:52777;;;36181:19;;;;36097:75;;;1949:52777;36097:75;;1949:52777;36097:75;;;;;;1949:52777;36097:75;;;:::i;:::-;;;1949:52777;;;;;;;;;;;;36097:75;;;1949:52777;;;;36097:75;;;-1:-1:-1;36097:75:9;;;1949:52777;;1624:4:10;1949:52777:9;;1624:4:10;;;;1949:52777:9;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;918:4:5;895:29;918:4;895:29;:::i;1949:52777:9:-;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;7377:66;1949:52777;;;;;;;7377:66;;1949:52777;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9860:26;;;;10009:24;1949:52777;9860:26;;1949:52777;;;;;;;;;;;;;;;;;;10009:24;;;;:::i;:::-;1949:52777;10009:48;10005:129;;1949:52777;;;;;;;;;;;;;10202:19;;1949:52777;10202:19;;;;;;:::i;:::-;1949:52777;10202:35;10198:103;;10440:23;;1949:52777;10440:23;;;;;:::i;:::-;10480:2;10466:11;1949:52777;10466:11;1949:52777;;10480:2;1949:52777;10466:11;1949:52777;;;;;10485:2;1949:52777;;;;;;10440:47;-1:-1:-1;10436:127:9;;10656:26;;;;;;247:2:5;10656:26:9;1949:52777;10656:26;;;;;:::i;:::-;219:30:5;;;10637:78:9;;10764:24;247:2:5;10764:24:9;1949:52777;10764:24;;;;;:::i;:::-;219:30:5;;;10745:76:9;;16581:24;;;;;;;;;;;;;;;;;;;;:::i;:::-;16667:26;;;;;:::i;:::-;1949:52777;;;;;1580:4:10;;;1949:52777:9;1580:4:10;;;;1624;;;;;;1949:52777:9;1624:4:10;;;;;;;1949:52777:9;;;;1624:4:10;;1949:52777:9;;;;;1580:4:10;;;1949:52777:9;;;247:2:5;1580:4:10;;1949:52777:9;;;1580:4:10;;;1949:52777:9;1580:4:10;;;;;;;;;;;;:::i;:::-;1624;1949:52777:9;;;;1580:4:10;;;;1949:52777:9;1580:4:10;;1949:52777:9;;1580:4:10;;;;;1949:52777:9;1580:4:10;;;;1949:52777:9;16455:10;1580:4:10;;;1949:52777:9;;1580:4:10;;;;;;;;;;:::i;:::-;;;;;;;1949:52777:9;1580:4:10;;;1949:52777:9;1580:4:10;;;1949:52777:9;17261:12;1949:52777;1624:4:10;;;1580;;;1624;;;;1580;;;1624;;;;;1580;;;1624;1580;;;1624;;;1580;1624;;;1580;;;;;;;;;;;;:::i;:::-;1949:52777:9;;;;;;;;;17791:25;;;;;;:::i;:::-;1949:52777;;17554:272;;;;;;;1624:4:10;17554:272:9;;;;;;;;:::i;:::-;;17580:9;;17554:4;;1949:52777;17554:272;;;;;;;;1949:52777;;;17554:272;;;;;;-1:-1:-1;17554:272:9;;;;;;;:::i;:::-;;;;;:::i;:::-;1949:52777;;;17554:272;;;;;;1949:52777;;;1624:4:10;;;;;;;;10745:76:9;1949:52777;;;10798:23;;;;10637:78;1949:52777;;;10692:23;;;;10436:127;10496:67;10539:23;;;;;:::i;:::-;1949:52777;;10496:67;;;;;;;;1949:52777;;;;;;;;;;;;;;;;;10496:67;;;;1949:52777;;;;;;;;;;10198:103;10246:55;10281:19;;;;;:::i;:::-;1949:52777;;10246:55;;;1949:52777;;;;10246:55;;;1949:52777;;;;;;;;;;;;;;;10005:129;10109:24;10066:68;10109:24;;;;:::i;1949:52777::-;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;10902:4;;;1949:52777;10902:4;1949:52777;10880:10;:27;10876:55;;1949:52777;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;1624:4:10;;;;;;;;;;;1949:52777:9;11066:26;;1949:52777;;;;;;;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;11096:69;11066:99;11062:138;;1949:52777;26764:42;1949:52777;26764:42;1949:52777;;26764:42;;;;;;;;;:::i;:::-;1949:52777;;;;;;;;;;;;;1624:4:10;;;;;;;;1949:52777:9;;;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;11062:138::-;1949:52777;;11174:26;;;;10876:55;1949:52777;;;10916:15;;;;1949:52777;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;8070:34;12478:70:6;;;:::i;:::-;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;8070:34;1949:52777;;;;;;;;;;35257:193;1949:52777;;;:::i;:::-;35257:193;;;;;;;;;;;:::i;1949:52777::-;;:::i;:::-;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;12478:70:6;;;;;:::i;:::-;247:2:5;219:30;;335:69;;1949:52777:9;;;;;;;;;;;;;;:::i;:::-;13543:96;;1949:52777;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;13761:63;1949:52777;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;:::i;:::-;13761:63;;;1949:52777;13886:4;1949:52777;13886:57;;;;;;1949:52777;;;;;13886:57;;;;;;;1624:4:10;13886:57:9;;;;1949:52777;;;;;;;;;;;:::i;:::-;13886:57;;;;;;;;;;;;1949:52777;;;13886:57;;;;:::i;:::-;1949:52777;;13886:57;1949:52777;1624:4:10;1949:52777:9;;1624:4:10;;;;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;13761:63;1949:52777;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1949:52777:9;;;;;;;247:2:5;1949:52777:9;;;;;;13543:96;1949:52777;;;13620:19;;;;335:69:5;1949:52777:9;;;381:23:5;;;;1949:52777:9;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;5558:48;1949:52777;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;7135:78;1949:52777;;;;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;11548:29;;1624:4:10;11548:29:9;;:4;1949:52777;11548:4;1949:52777;11548:29;;;;;;;;1949:52777;11548:29;;;1949:52777;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;11548:29;;;;;;;;;;;;;;;;;;;:::i;:::-;;;1949:52777;;;;;;;;;;:::i;:::-;;;1624:4:10;11548:29:9;;;;;;;;;;;1949:52777;;1624:4:10;;;;;;;;1949:52777:9;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;11523:61:6;1949:52777:9;11523:61:6;;1949:52777:9;;;;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;6100:2;1949:52777;;;;;;-1:-1:-1;;1949:52777:9;;;;;12478:70:6;;:::i;:::-;6813:405;;;;;;;;;;1949:52777:9;;;;;-1:-1:-1;;1949:52777:9;;;;;9831:339:6;;;;;;;;;;;;;;;;1949:52777:9;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;12478:70:6;;;:::i;:::-;5450:20:9;8888:57;;;8884:101;;5199:19;9110:56;;9106:99;;5199:19;;1949:52777;5199:19;9290:47;5199:19;9216:50;5199:19;1949:52777;;;;9290:47;1949:52777;;9106:99;1949:52777;;9175:30;;;;8884:101;1949:52777;;8954:31;;;;1949:52777;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;9860:26;;;;1949:52777;;;;;;;;;;;;;;;;;10009:24;;;;;:::i;:::-;1949:52777;10009:48;10005:129;;1949:52777;;;;;;;;;;;;;10202:19;;1949:52777;10202:19;;;;;;:::i;:::-;1949:52777;10202:35;10198:103;;10440:23;;;;;;;;:::i;:::-;10480:2;10466:11;1949:52777;10466:11;1949:52777;;10480:2;1949:52777;10466:11;1949:52777;;;;;10485:2;1949:52777;;;;;;10440:47;-1:-1:-1;10436:127:9;;10656:26;;1949:52777;10656:26;;;247:2:5;10656:26:9;;;;:::i;:::-;219:30:5;;;10637:78:9;;1949:52777;10764:24;;247:2:5;10764:24:9;;;;:::i;:::-;219:30:5;;;10745:76:9;;20142:24;;1624:4:10;20142:24:9;20543:272;20142:24;;20222:26;20142:24;;;;;;;;;1624:4:10;20142:24:9;;:::i;:::-;20222:26;;;;;;:::i;:::-;1949:52777;;;;1624:4:10;;;1949:52777:9;;1624:4:10;;1949:52777:9;;1624:4:10;;;1949:52777:9;1624:4:10;;;;1949:52777:9;20047:10;1624:4:10;;;1949:52777:9;1624:4:10;;;;;;;;;;;;;;;1949:52777:9;;;1624:4:10;;;1949:52777:9;;1624:4:10;;;;1949:52777:9;9860:26;1624:4:10;;;;1949:52777:9;;;1624:4:10;;;1949:52777:9;1624:4:10;20439:12:9;1949:52777;1624:4:10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;1949:52777:9;;;;;;20780:25;;1949:52777;;;20780:25;;;:::i;:::-;1949:52777;;;20543:272;;;;;1624:4:10;20543:272:9;;;;;:::i;:::-;;20569:9;;1949:52777;20543:4;1949:52777;20543:272;;;;;;;;1949:52777;;;10745:76;1949:52777;;;10798:23;;;;10637:78;1949:52777;;;10692:23;;;;10436:127;10496:67;10539:23;;;;;:::i;1949:52777::-;;;;;;;;;;10198:103;10246:55;10281:19;;;;;:::i;10005:129::-;10109:24;10066:68;10109:24;;;;:::i;1949:52777::-;;;;;;;-1:-1:-1;;1949:52777:9;;;;;9239:383:6;;;;;;7972:9;9132:15;1949:52777:9;9239:383:6;;;;;;;;;1949:52777:9;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;6397:3;1949:52777;;;;;;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;;:::i;:::-;10902:4;1949:52777;10902:4;1949:52777;10880:10;:27;10876:55;;1949:52777;25200:928;1949:52777;;;25200:928;:::i;10876:55::-;10916:15;;;;;;1949:52777;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;-1:-1:-1;;1949:52777:9;;;;;;;;6200:4;1949:52777;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;-1:-1:-1;;1949:52777:9;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;-1:-1:-1;1949:52777:9;;;;;-1:-1:-1;1949:52777:9;;;-1:-1:-1;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;1949:52777:9;;;;;;;;-1:-1:-1;1949:52777:9;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;:::o;25200:928::-;1949:52777;;;;;25720:18;;1949:52777;25752:18;25748:240;;1949:52777;;26117:1;1949:52777;;;26117:1;1949:52777;26061:60;1949:52777;;;;26061:60;;:::i;:::-;25200:928::o;25748:240::-;1949:52777;25791:22;1949:52777;;;;;;25791:22;1949:52777;25973:1;1949:52777;;;25973:1;1949:52777;25917:60;1949:52777;;;;25917:60;;:::i;1949:52777::-;;25736:1;1949:52777;;;;;25736:1;1949:52777;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;1624:4:10:-;;1949:52777:9;;;1624:4:10;;;;;:::o;:::-;;;;;;;;;;;;;;;;;:::o;:::-;;1949:52777:9;;;;;;;;1624:4:10:o;:::-;;;;;;;;;;;1949:52777:9;;;1624:4:10;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;1949:52777:9;;;;;;:::i;:::-;;1624:4:10;;;1949:52777:9;;1624:4:10;;;1949:52777:9;:::i;:::-;;1624:4:10;;;1949:52777:9;1624:4:10;;;1949:52777:9;;;;;;;;;;1624:4:10;;;;;1949:52777:9;1624:4:10;;;;;:::i;:::-;1949:52777:9;;;;1624:4:10;;;1949:52777:9;1624:4:10;;;;;:::i;:::-;1949:52777:9;1624:4:10;;;1949:52777:9;1624:4:10;;1949:52777:9;;;;;;1624:4:10;;;;;;;;;;;1949:52777:9;1624:4:10;;;:::o;1949:52777:9:-;;;;;;;;;;:::o;:::-;;;;;;;;;;;1624:4:10;-1:-1:-1;1624:4:10;;;;1949:52777:9;;:::o;:::-;;;;;;;;;;;;;;1624:4:10;-1:-1:-1;1624:4:10;;;;;;1949:52777:9;;;;;:::o;:::-;;;;;;;;;;;;;:::o;5953:1::-;;;;;;;;;;:::o;38277:8063::-;;;;;;;;;39191:193;;;;;;;;;;:::i;:::-;1949:52777;;-1:-1:-1;1949:52777:9;41523:19;1949:52777;;;-1:-1:-1;1949:52777:9;41695:22;;;;1949:52777;;;41735:19;41731:1189;;38277:8063;43456:7;;1949:52777;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;43327:239;1624:4:10;43327:239:9;;;;;;1949:52777;;;;5953:1;;;1949:52777;5953:1;;;;1949:52777;;;;;;;;;;5953:1;;;1949:52777;;;5953:1;1949:52777;-1:-1:-1;1949:52777:9;;;43327:239;;;;;;;-1:-1:-1;43327:239:9;;;38277:8063;41695:22;;;;1949:52777;;;;43909:76;;44260:41;1949:52777;41695:22;1949:52777;44029:56;44061:23;1949:52777;44037:12;44029:56;:::i;:::-;5199:19;;;1949:52777;5953:1;;;;;;44240:10;5953:1;41695:22;;;;5953:1;-1:-1:-1;5953:1:9;44260:41;5953:1;1949:52777;;;;;;;;;;;;;;6200:4;1949:52777;8326:988:8;1949:52777:9;;5953:1;8326:988:8;44834:4:9;1949:52777;8326:988:8;44240:10:9;5953:1;8326:988:8;;;;;;1949:52777:9;-1:-1:-1;5953:1:9;8326:988:8;;;;;;;41695:22:9;-1:-1:-1;8326:988:8;;;;;;;-1:-1:-1;5953:1:9;8326:988:8;1949:52777:9;8326:988:8;45052:49:9;;1949:52777;;45052:49;;:::i;:::-;1949:52777;;;;45426:15;;;;;;:::i;:::-;1949:52777;44061:23;1949:52777;;;5953:1;;;;45824:19;;45820:273;;38277:8063;1949:52777;;;;;;;;;;;;;;;5953:1;;1949:52777;;;5953:1;;1949:52777;5953:1;;;1949:52777;;5953:1;;;1949:52777;;5953:1;;1949:52777;44240:10;46116:217;5953:1;44240:10;46116:217;;38277:8063::o;45820:273::-;1949:52777;45904:4;1949:52777;;;;;;;44061:23;1949:52777;;5953:1;;;;44061:23;5953:1;1949:52777;5953:1;;;;1949:52777;;;;45904:4;1949:52777;;;;;;;45999:83;;;;;5953:1;1949:52777;-1:-1:-1;1949:52777:9;;;;;45999:83;;;;;;;1624:4:10;45999:83:9;;43327:239;45999:83;;1949:52777;5953:1;;;;;45904:4;1949:52777;5953:1;;;1949:52777;;;;5953:1;:::i;:::-;41695:22;5953:1;;;;45999:83;;;;;;;;1949:52777;45999:83;;;;;;;45820:273;;;;;;;;45999:83;;;;:::i;:::-;;;;;1949:52777;;1624:4:10;-1:-1:-1;1624:4:10;;;;;8326:988:8;;-1:-1:-1;8326:988:8;43327:239:9;8326:988:8;;43909:76:9;43327:239;1949:52777;;43960:25;;;;43327:239;;;;1949:52777;43327:239;;1949:52777;43327:239;;;;;;1949:52777;43327:239;;;:::i;:::-;;;5953:1;;;;1624:4:10;43327:239:9;;;;;;;-1:-1:-1;43327:239:9;;1949:52777;;-1:-1:-1;1949:52777:9;;;;;-1:-1:-1;1949:52777:9;41731:1189;5953:1;42053:50;;42052:88;;;41731:1189;42048:858;;;1949:52777;42405:12;;1949:52777;5953:1;;;;;;1949:52777;42364:54;;42360:404;;42048:858;;;;;41731:1189;;;42360:404;-1:-1:-1;;5953:1:9;1949:52777;42652:54;;42648:93;;42360:404;;;;;42648:93;5953:1;1949:52777;;42715:26;;;;42048:858;1949:52777;42839:12;1949:52777;42814:38;42048:858;42810:77;5953:1;1949:52777;;42861:26;;;;42052:88;42109:30;5953:1;42109:30;;42052:88;;428:387:5;1949:52777:9;;;;;;;;;650:1:5;1949:52777:9;;;;;;;;;;;;;;;;;;;;;;;;;;428:387:5;:::o;3289:3:10:-;;;;1949:52777:9;1624:4:10;3289:3;;;;1949:52777:9;3289:3:10;1949:52777:9;;3289:3:10;;;;;;;;;1949:52777:9;;;;1624:4:10;-1:-1:-1;1624:4:10;;;;1949:52777:9;;;3289:3:10;;;;1949:52777:9;;3289:3:10;;1949:52777:9;;3289:3:10;;;1949:52777:9;1624:4:10;3289:3;;;:::o;4053:::-;;1949:52777:9;1624:4:10;4053:3;;;;;;1949:52777:9;;4053:3:10;;;;;;;;;1949:52777:9;;;;1624:4:10;-1:-1:-1;1624:4:10;;;;1949:52777:9;;;4053:3:10;;;;1949:52777:9;;4053:3:10;;1949:52777:9;1624:4:10;4053:3;;3289;4053:::o;23010:1909:9:-;;;1949:52777;;;;;23124:17;;1949:52777;;;2023:2:10;1949:52777:9;;;1925:2:10;1949:52777:9;;5953:1;;;23320:26;;23316:758;;1949:52777;24087:30;;24083:660;;1949:52777;;;;24889:23;;;;;;;1949:52777;24889:23;24083:660;1949:52777;;;;;;2478:3:10;1949:52777:9;;;;2565:3:10;1949:52777:9;;;3956:3:10;1949:52777:9;;;;;;4053:3:10;1949:52777:9;;;24140:592;;;;;;1949:52777;1801:1:10;1949:52777:9;;;;24140:592;;;;;;;1624:4:10;24140:592:9;;1949:52777;;3289:3:10;1949:52777:9;;;3908:3:10;1949:52777:9;;2565:3:10;1949:52777:9;2293:3:10;2478;1949:52777:9;;2565:3:10;1949:52777:9;;24140:592;1949:52777;24140:592;;;:::i;:::-;;;;;;;;;;;24133:599;:::o;24140:592::-;;;;:::i;23316:758::-;1949:52777;;;;2478:3:10;1949:52777:9;;;;;;;2565:3:10;1949:52777:9;;;;2941:3:10;1949:52777:9;;;3192:3:10;1949:52777:9;;;;;;3289:3:10;1949:52777:9;;;23369:694;;;;;;1949:52777;1801:1:10;1949:52777:9;;;;23369:694;;;;;;;1624:4:10;23369:694:9;;1949:52777;;3289:3:10;1949:52777:9;;;3088:3:10;1949:52777:9;;5953:1;;;1949:52777;2893:3:10;1949:52777:9;;2565:3:10;1949:52777:9;2293:3:10;2478;1949:52777:9;;2565:3:10;1949:52777:9;;23369:694;1949:52777;23369:694;;;:::i;20948:1936::-;;;1949:52777;;;;;21062:17;;1949:52777;;;2023:2:10;1949:52777:9;;;1925:2:10;1949:52777:9;;5953:1;;;21258:26;;21254:771;;1949:52777;22038:30;;22034:674;;1949:52777;;;;22854:23;;;;;;;1949:52777;22854:23;22034:674;1949:52777;;;;;;2478:3:10;1949:52777:9;;;;2565:3:10;1949:52777:9;;;3956:3:10;1949:52777:9;;;;;;4053:3:10;1949:52777:9;;;22084:593;;;;;;1949:52777;1801:1:10;1949:52777:9;;;;22084:593;;;;;;;1624:4:10;22084:593:9;;1949:52777;;3289:3:10;1949:52777:9;;;3908:3:10;1949:52777:9;;2565:3:10;1949:52777:9;2293:3:10;2478;1949:52777:9;;2565:3:10;1949:52777:9;;22084:593;1949:52777;22084:593;;;:::i;21254:771::-;1949:52777;;;;2478:3:10;1949:52777:9;;;;;;;2565:3:10;1949:52777:9;;;;2941:3:10;1949:52777:9;;;3192:3:10;1949:52777:9;;;;;;3289:3:10;1949:52777:9;;;21300:694;;;;;;1949:52777;1801:1:10;1949:52777:9;;;;21300:694;;;;;;;1624:4:10;21300:694:9;;1949:52777;;3289:3:10;1949:52777:9;;;3088:3:10;1949:52777:9;;5953:1;;;1949:52777;2893:3:10;1949:52777:9;;2565:3:10;1949:52777:9;2293:3:10;2478;1949:52777:9;;2565:3:10;1949:52777:9;;21300:694;1949:52777;21300:694;;;:::i;7292:355:6:-;7390:251;;;;;;7292:355::o;7390:251::-;;;;;;;34235:612:9;;;;;;;;;;;5953:1;1949:52777;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1624:4:10;;;;1949:52777:9;;;1624:4:10;1949:52777:9;;;1624:4:10;;;1949:52777:9;1624:4:10;;-1:-1:-1;1624:4:10;;1949:52777:9;;;;;;;;;;;;:::i;:::-;;34541:299;;34235:612;:::o;27234:1587::-;;;1949:52777;;;;;27383:17;;1949:52777;;;2293:3:10;1949:52777:9;;;;27553:2;2293:3:10;1949:52777:9;;27521:27;1949:52777;;27515:40;27511:69;;1949:52777;5953:1;1949:52777;;;2336:3:10;1949:52777:9;;2565:3:10;27615:94:9;;1949:52777;2379:3:10;1949:52777:9;;;5953:1;1949:52777;;2379:3:10;1949:52777:9;;27751:97;;28198:26;;28194:125;;1949:52777;28332:30;;;28328:133;;28785:29;;;27234:1587;:::o;28328:133::-;28403:47;;;;:::i;:::-;28378:72;:::o;28194:125::-;28265:43;;;;:::i;27751:97::-;27832:16;1949:52777;;27832:16;;;;11375:939:8;11502:806;11375:939;11502:806;11375:939;11502:806;;;;;;;;;;;;;;;;;;;;;;;;;;;;11375:939::o;11502:806::-;;;;;;;52549:2175:9;;;1949:52777;2023:2:10;1949:52777:9;;;;2293:3:10;1949:52777:9;;;2150:3:10;1949:52777:9;;5953:1;;;3435:3:10;1949:52777:9;;;;;;2565:3:10;1949:52777:9;;;;;;53238:54;1624:4:10;53238:54:9;;2565:3:10;1949:52777:9;;53180:29;1949:52777;;53238:54;;;2747:3:10;53238:54:9;;2747:3:10;53238:54:9;;;;;;;;;-1:-1:-1;53238:54:9;;;52549:2175;1949:52777;;2478:3:10;1949:52777:9;;;;2565:3:10;1949:52777:9;;;;2893:3:10;1949:52777:9;;;;54096:181;1949:52777;3289:3:10;1949:52777:9;;5953:1;;;1949:52777;53780:189;1843:1:10;1949:52777:9;;;;;;;;;;2379:3:10;1949:52777:9;;5953:1;;;2796:3:10;1949:52777:9;;2565:3:10;2478;1949:52777:9;;2565:3:10;53780:189:9;;;:::i;:::-;54096:181;:::i;:::-;54292:17;54288:205;;54688:29;;;-1:-1:-1;52549:2175:9;:::o;54288:205::-;54431:51;;;:::i;53238:54::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;2747:3:10;;;;1949:52777:9;;;:::i;:::-;53238:54;;;;;;;-1:-1:-1;53238:54:9;;4199:3:10;;;;;;;;;;:::i;:::-;1949:52777:9;;;;;;;:::i;:::-;4199:3:10;;;-1:-1:-1;4199:3:10;;;;:::o;:::-;1949:52777:9;4199:3:10;:::o;31380:2456:9:-;;-1:-1:-1;1949:52777:9;;2023:2:10;1949:52777:9;;;;2293:3:10;1949:52777:9;;;;2478:3:10;1949:52777:9;;;;2565:3:10;1949:52777:9;;;3762:3:10;1949:52777:9;;;;;;3908:3:10;1949:52777:9;;;;;3956:3:10;1949:52777:9;;;;;;4053:3:10;1949:52777:9;;;;;;;;;;;;31826:862;;;;;1624:4:10;31826:862:9;;1949:52777;31826:862;;1949:52777;3859:3:10;;;;;;;;;1843:1;1949:52777:9;;3859:3:10;:::i;:::-;1949:52777:9;2379:3:10;1949:52777:9;;5953:1;;;3859:3:10;;;1949:52777:9;2478:3:10;1949:52777:9;;2565:3:10;3859;;;1949:52777:9;2565:3:10;1949:52777:9;;2565:3:10;3859;;;1949:52777:9;;;2565:3:10;3859;;;1949:52777:9;;;2565:3:10;2478;3859;;1949:52777:9;;;3289:3:10;1949:52777:9;;2565:3:10;3859;;3289;31826:862:9;1949:52777;;2150:3:10;1949:52777:9;;5953:1;;;31826:862;;;;;;;31380:2456;-1:-1:-1;31822:2008:9;;33756:74;;;;;33802:17;33756:74;;:::i;:::-;33802:17;:::i;31822:2008::-;4150:3:10;1949:52777:9;;;;;;;;5953:1;;;32847:15;;32843:877;;31822:2008;33733:11;;;;;;:::o;32843:877::-;5953:1;1949:52777;;;;;;;;;5953:1;;;;;;;;;;;;1949:52777;;;;;;;;33486:88;;;;;1949:52777;;;;;;5953:1;1949:52777;;33486:88;;;;;;;1624:4:10;33486:88:9;;1949:52777;33486:88;;1949:52777;5953:1;3859:3:10;5953:1:9;;;1949:52777;3859:3:10;5953:1:9;;1949:52777;;;;5953:1;:::i;:::-;;3859:3:10;5953:1:9;;;33486:88;;;;;;;;;;32843:877;;;;;;;33486:88;;;;:::i;:::-;;;;;1949:52777;;1624:4:10;1949:52777:9;;1624:4:10;;;;5953:1:9;3859:3:10;1949:52777:9;;;;;;;;31826:862;;;;1949:52777;31826:862;;1949:52777;31826:862;;;;;;1949:52777;31826:862;;;:::i;:::-;;;5953:1;;;;1624:4:10;31826:862:9;;;;;;;-1:-1:-1;31826:862:9;;50143:2400;;;;;;;-1:-1:-1;1949:52777:9;;;;50455:19;1949:52777;;;;;;;;50700:24;;;;1949:52777;;;;;;;50815:22;;;50811:59;;1949:52777;;;5953:1;;;51203:12;5953:1;;;;;;;1949:52777;;51314:131;;;;;;1949:52777;;;;;;;;;;;;;51314:131;;;;;1624:4:10;51314:131:9;;;;;1949:52777;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1624:4:10;;;;;;51314:131:9;;;;;;;;50143:2400;1949:52777;6100:2;1949:52777;;;;;;6100:2;1949:52777;;;;;;;52444:51;1949:52777;;;6200:4;52245:57;52415:12;1949:52777;;;52319:37;1949:52777;;52245:57;;:::i;:::-;52307:2;1949:52777;;;52319:37;:::i;:::-;52415:12;;:::i;:::-;52444:51;;50700:24;50143:2400;:::o;1949:52777::-;;;;;;;51314:131;1949:52777;;51314:131;;;;;;;:::i;:::-;;;;;;1949:52777;;1624:4:10;1949:52777:9;;1624:4:10;;;;50811:59:9;50839:31;;;;;;;;;;;;;:::o;28827:2547::-;;-1:-1:-1;1949:52777:9;;2023:2:10;1949:52777:9;;;;2293:3:10;1949:52777:9;;;;2565:3:10;1949:52777:9;;;;;2478:3:10;1949:52777:9;;;;2565:3:10;1949:52777:9;;;2893:3:10;1949:52777:9;;;;;;2941:3:10;1949:52777:9;;;;;;3192:3:10;1949:52777:9;;;;;;3289:3:10;1949:52777:9;;;;;;;2990:3:10;1949:52777:9;;29277:960;;;;;1624:4:10;29277:960:9;;1949:52777;29277:960;;1949:52777;2990:3:10;;;;;;;;;1843:1;1949:52777:9;;2990:3:10;:::i;:::-;1949:52777:9;2565:3:10;1949:52777:9;;29540:29;1949:52777;;2990:3:10;;;2747;2379;1949:52777:9;;5953:1;;;2990:3:10;;;1949:52777:9;2478:3:10;1949:52777:9;;2565:3:10;2990;;;1949:52777:9;2796:3:10;1949:52777:9;;2565:3:10;2990;;;1949:52777:9;;;2565:3:10;2478;2990;;1949:52777:9;-1:-1:-1;;2990:3:10;;;;2565;2990;;;1949:52777:9;;2990:3:10;:::i;:::-;1949:52777:9;;;3289:3:10;1949:52777:9;;2990:3:10;;;3289;29277:960:9;1949:52777;;2150:3:10;1949:52777:9;;5953:1;;;29277:960;;;;;;;28827:2547;-1:-1:-1;29273:2095:9;;31294:74;;;;31340:17;31294:74;;:::i;29273:2095::-;3483:3:10;1949:52777:9;;;;;;3435:3:10;1949:52777:9;;5953:1;;;30390:15;;30386:872;;31271:11;;;;;;:::o;30386:872::-;5953:1;1949:52777;;;;;;;;;5953:1;;;;;;;;;;;;1949:52777;;;;;;;;31029:83;;;;;1949:52777;;;;;;5953:1;1949:52777;;31029:83;;;;;;;1624:4:10;31029:83:9;;1949:52777;31029:83;;1949:52777;5953:1;2990:3:10;5953:1:9;;;1949:52777;2990:3:10;5953:1:9;;1949:52777;;;;5953:1;:::i;29277:960::-;;;;1949:52777;29277:960;;1949:52777;29277:960;;;;;;1949:52777;29277:960;;;:::i;:::-;;;5953:1;;;;1624:4:10;29277:960:9;;;;;;;-1:-1:-1;29277:960:9;;1949:52777;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;-1:-1:-1;1949:52777:9:o;11903:983::-;12162:11;11903:983;12162:11;:::i;:::-;12538:61;1949:52777;;12568:29;12545:53;;;;;;;;;;:::i;:::-;12538:61;:::i;:::-;1949:52777;;;;;;;;12538:80;;12534:97;;1949:52777;;12645:59;1949:52777;;12675:27;12545:53;12652:51;;;12545:53;12652:51;;;;;:::i;12645:59::-;1949:52777;12645:78;12641:95;;12750:58;1949:52777;;12780:26;12545:53;12757:50;;;12545:53;12757:50;;;;;:::i;12750:58::-;1949:52777;12750:77;12746:94;;1949:52777;11903:983;:::o;12746:94::-;1949:52777;12829:11;:::o;12641:95::-;12725:11;;1949:52777;12725:11;:::o;12534:97::-;12620:11;;1949:52777;12620:11;:::o
Swarm Source
ipfs://808e4fc618233f231535d012f6c96d52b17d5442bd4565852c1ba551e5a0ba06
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ 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.