Source Code
Latest 21 from a total of 21 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Create Campaign | 28603279 | 60 days ago | IN | 0 FRAX | 0.00006422 | ||||
| Create Campaign | 27914909 | 76 days ago | IN | 0 FRAX | 0.00002988 | ||||
| Create Campaign | 27914273 | 76 days ago | IN | 0 FRAX | 0.00002314 | ||||
| Create Campaign | 27867454 | 77 days ago | IN | 0 FRAX | 0.00024337 | ||||
| Create Campaign | 27564139 | 84 days ago | IN | 0 FRAX | 0.00008271 | ||||
| Create Campaign | 27520618 | 85 days ago | IN | 0 FRAX | 0.00027246 | ||||
| Create Campaign | 27391504 | 88 days ago | IN | 0 FRAX | 0.00010943 | ||||
| Create Campaign | 26958596 | 98 days ago | IN | 0 FRAX | 0.00010834 | ||||
| Create Campaign | 26508057 | 109 days ago | IN | 0 FRAX | 0.00006492 | ||||
| Create Campaign | 26224609 | 115 days ago | IN | 0 FRAX | 0.0001424 | ||||
| Create Campaign | 26171820 | 117 days ago | IN | 0 FRAX | 0.00539203 | ||||
| Create Campaign | 26152733 | 117 days ago | IN | 0 FRAX | 0.00006392 | ||||
| Create Campaign | 25885936 | 123 days ago | IN | 0 FRAX | 0.00005021 | ||||
| Create Campaign | 25885404 | 123 days ago | IN | 0 FRAX | 0.00003937 | ||||
| Create Campaign | 25403428 | 134 days ago | IN | 0 FRAX | 0.00009865 | ||||
| Create Campaign | 25398648 | 135 days ago | IN | 0 FRAX | 0.00026191 | ||||
| Create Campaign | 25339094 | 136 days ago | IN | 0 FRAX | 0.00004072 | ||||
| Create Campaign | 25331932 | 136 days ago | IN | 0 FRAX | 0.00005204 | ||||
| Create Campaign | 25276051 | 137 days ago | IN | 0 FRAX | 0.00008432 | ||||
| Create Campaign | 25274546 | 137 days ago | IN | 0 FRAX | 0.00015177 | ||||
| Create Campaign | 25271974 | 138 days ago | IN | 0 FRAX | 0.00027378 |
Latest 22 internal transactions
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 28603279 | 60 days ago | Contract Creation | 0 FRAX | |||
| 27914909 | 76 days ago | Contract Creation | 0 FRAX | |||
| 27914273 | 76 days ago | Contract Creation | 0 FRAX | |||
| 27867454 | 77 days ago | Contract Creation | 0 FRAX | |||
| 27564139 | 84 days ago | Contract Creation | 0 FRAX | |||
| 27520618 | 85 days ago | Contract Creation | 0 FRAX | |||
| 27391504 | 88 days ago | Contract Creation | 0 FRAX | |||
| 26958596 | 98 days ago | Contract Creation | 0 FRAX | |||
| 26508057 | 109 days ago | Contract Creation | 0 FRAX | |||
| 26224609 | 115 days ago | Contract Creation | 0 FRAX | |||
| 26171820 | 117 days ago | Contract Creation | 0 FRAX | |||
| 26152733 | 117 days ago | Contract Creation | 0 FRAX | |||
| 25885936 | 123 days ago | Contract Creation | 0 FRAX | |||
| 25885404 | 123 days ago | Contract Creation | 0 FRAX | |||
| 25403428 | 134 days ago | Contract Creation | 0 FRAX | |||
| 25398648 | 135 days ago | Contract Creation | 0 FRAX | |||
| 25339094 | 136 days ago | Contract Creation | 0 FRAX | |||
| 25331932 | 136 days ago | Contract Creation | 0 FRAX | |||
| 25276051 | 137 days ago | Contract Creation | 0 FRAX | |||
| 25274546 | 137 days ago | Contract Creation | 0 FRAX | |||
| 25271974 | 138 days ago | Contract Creation | 0 FRAX | |||
| 25214342 | 139 days ago | Contract Creation | 0 FRAX |
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
EchoCampaignFactory
Compiler Version
v0.8.30+commit.73712a01
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
// ███████╗ ██████╗██╗ ██╗ ██████╗ ███╗ ███╗ █████╗ ██████╗ ██╗ ██╗███████╗████████╗
// ██╔════╝██╔════╝██║ ██║██╔═══██╗ ████╗ ████║██╔══██╗██╔══██╗██║ ██╔╝██╔════╝╚══██╔══╝
// █████╗ ██║ ███████║██║ ██║ ██╔████╔██║███████║██████╔╝█████╔╝ █████╗ ██║
// ██╔══╝ ██║ ██╔══██║██║ ██║ ██║╚██╔╝██║██╔══██║██╔══██╗██╔═██╗ ██╔══╝ ██║
// ███████╗╚██████╗██║ ██║╚██████╔╝ ██║ ╚═╝ ██║██║ ██║██║ ██║██║ ██╗███████╗ ██║
// ╚══════╝ ╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚═╝
// ============================== EchoCampaignFactory V2 =============================
// =================================== Summer 2025 ===================================
import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol";
import {IEchoAdministration} from "../interfaces/IEchoAdministration.sol";
import {InitCampaign, IEchoCampaignFactory} from "../interfaces/IEchoCampaignFactory.sol";
import {QA_METHOD, CampaignInitData, IEchoCampaign} from "../interfaces/IEchoCampaign.sol";
import {ClonesWithImmutableArgs} from "../libs/ClonesWithImmutableArgs.sol";
import {EchoContents} from "./EchoContents.sol";
/// @title Echo Campaign Factory contract
/// @author Dynabits.org
contract EchoCampaignFactory is IEchoCampaignFactory {
/********************************\
|-*-*-*-*-* STATES *-*-*-*-*-|
\********************************/
address public implementation;
address[] public allCampaigns;
mapping(bytes32 => string) public campaignNameHashToName;
mapping(string => CampaignInitData) private _campaignNameToInitData;
/*******************************\
|-*-*-*-* CONSTANTS *-*-*-*-|
\*******************************/
address public immutable ECHO_ADMIN;
address public immutable ECHO_CONTENTS;
IEchoCampaignFactory private constant ECHO_FACTORY_V1 =
IEchoCampaignFactory(0x8F1E629536C74e8d8f58cfcbb44aD9CDf66a2433);
/********************************\
|-*-*-*-*-* EVENTS *-*-*-*-*-|
\********************************/
event ImplementationChanged(
address indexed lastImplementation,
address indexed newImplementation
);
event CampaignCreated(
address indexed clonedCampaign,
address indexed owner,
address indexed implementation,
string name,
uint256 applicationFee,
string EchoMarketData,
uint256 totalClonedCampaigns,
InitCampaign initCampaign
);
/********************************\
|-*-*-*-*-* ERRORS *-*-*-*-*-|
\********************************/
error ONLY_PROTOCOL_ADMIN();
error ZERO_ADDRESS_PROVIDED();
error NAME_LENGTH_LOWER_THAN_8_BYTES();
error NULL_CID();
error NAME_LENGTH_LONGER_THAN_64_BYTES();
error NAME_EXISTS(string name, address clonedCampaign);
error START_TIME_IN_PAST();
error END_TIME_LOWER_THAN_START_TIME();
error CHECK_MAX_CAMPAIGN_DURATION(uint256 maxCampaignTime);
error ONLY_WHITELISTED_TOKEN(address token);
error QA_METHOD_OR_ORACLE_NOT_ALLOWED(string qaActionKind);
error SUM_OF_QUALIFICATION_PARAMS_MUST_BE_100(uint256 sumOfPct);
error SUM_OF_SOCIAL_KPI_PARAMS_MUST_BE_100(uint256 sumOfPct);
error MAX_PER_POST_BIGGER_THAN_RESERVED_AMOUNT();
error NO_QUALIFICATION();
error NO_KPIS();
error SOCIAL_KPI_RATIO_IS_ZERO(string kpiActionKind);
error ONLY_ALLOWED_SOCIAL_KPIS();
/*******************************\
|-*-*-*-* MODIFIERS *-*-*-*-|
\*******************************/
modifier onlyProtocolAdmin() {
if (msg.sender != _protocolAdmin()) revert ONLY_PROTOCOL_ADMIN();
_;
}
/******************************\
|-*-*-*-* BUILT-IN *-*-*-*-|
\******************************/
constructor(address echoAdmin, address currentImplementation) {
_revertZeroAddress(echoAdmin);
_revertZeroAddress(currentImplementation);
ECHO_ADMIN = echoAdmin;
ECHO_CONTENTS = address(new EchoContents(echoAdmin));
implementation = currentImplementation;
for (uint256 i; i < 24; ) {
allCampaigns.push(ECHO_FACTORY_V1.allCampaigns(i));
unchecked {
i++;
}
}
}
/********************************\
|-*-*-* ADMINISTRATION *-*-*-|
\********************************/
/// @inheritdoc IEchoCampaignFactory
function changeImplementation(address newImplementation)
external
onlyProtocolAdmin
{
_revertZeroAddress(newImplementation);
emit ImplementationChanged(implementation, newImplementation);
implementation = newImplementation;
}
/*******************************\
|*-*-*-* EXTERNALS *-*-*-*-*|
\*******************************/
/// @inheritdoc IEchoCampaignFactory
function createCampaign(
address owner,
string calldata name,
InitCampaign memory initCampaign
) external returns (address clonedCampaign) {
// Ensure the owner address is not a zero address.
_revertZeroAddress(owner);
// Ensure the refund address is valid if maxPerPost is set.
if (initCampaign.budgetInfo.maxPerPost != 0)
_revertZeroAddress(initCampaign.refundAddress);
else initCampaign.refundAddress = address(0);
// Ensure the campaign name length is between 8 and 64 characters.
if (bytes(name).length < 8) revert NAME_LENGTH_LOWER_THAN_8_BYTES();
if (bytes(name).length > 64) revert NAME_LENGTH_LONGER_THAN_64_BYTES();
// Ensure the campaign has a valid IPFS CID for referencing content.
if (bytes(initCampaign.ipfsCID).length == 0) revert NULL_CID();
// Ensure the campaign name is unique and has not been used before.
if (campaignNameToInitData(name).clonedCampaign != address(0))
revert NAME_EXISTS(
name,
campaignNameToInitData(name).clonedCampaign
);
// Ensure the campaign start time is not in the past.
if (initCampaign.startTime < block.timestamp)
revert START_TIME_IN_PAST();
// Ensure the campaign end time is later than the start time.
if (initCampaign.endTime <= initCampaign.startTime)
revert END_TIME_LOWER_THAN_START_TIME();
// Validate that the campaign duration does not exceed the max allowed duration.
if (
IEchoAdministration(ECHO_ADMIN).maxCampaignTime() != 0 &&
IEchoAdministration(ECHO_ADMIN).maxCampaignTime() <
initCampaign.endTime - initCampaign.startTime
)
revert CHECK_MAX_CAMPAIGN_DURATION(
IEchoAdministration(ECHO_ADMIN).maxCampaignTime()
);
// Ensure the budget token is whitelisted for use.
_onlyWhitelistedToken(initCampaign.budgetInfo.token);
// Ensure maxPerPost does not exceed the reservedAmount if maxPerPost is set.
if (
initCampaign.budgetInfo.maxPerPost != 0 &&
(initCampaign.budgetInfo.reservedAmount <
initCampaign.budgetInfo.maxPerPost)
) revert MAX_PER_POST_BIGGER_THAN_RESERVED_AMOUNT();
// Ensure at least one qualification method is provided.
if (initCampaign.qaData.length == 0) revert NO_QUALIFICATION();
uint256 totalPcts;
uint256 i;
string memory qaAction;
bytes32[] memory qaData = new bytes32[](1);
while (i < initCampaign.qaData.length) {
// Determine the qualification method type and concatenate it with the kind.
if (initCampaign.qaData[i].method == QA_METHOD.AI)
qaAction = "QA-AI: ";
else if (initCampaign.qaData[i].method == QA_METHOD.COMMUNITY)
qaAction = "QA-C: ";
else qaAction = "QA-V: ";
qaData[0] = keccak256(
abi.encodePacked(
string.concat(qaAction, initCampaign.qaData[i].kind)
)
);
// Ensure the oracle is allowed to use the specified qualification method.
if (
!IEchoAdministration(ECHO_ADMIN).allowedActionKind(
initCampaign.qaData[i].oracle,
qaData
)
)
revert QA_METHOD_OR_ORACLE_NOT_ALLOWED(
string.concat(qaAction, initCampaign.qaData[i].kind)
);
totalPcts += initCampaign.qaData[i].pct;
unchecked {
i++;
}
}
// Ensure the sum of qualification percentages is exactly 100%.
if (totalPcts != 100)
revert SUM_OF_QUALIFICATION_PARAMS_MUST_BE_100(totalPcts);
delete i;
delete totalPcts;
// Ensure at least one KPI is provided.
if (initCampaign.socialKPIs.kpis.length == 0) revert NO_KPIS();
bytes32[] memory kpis = new bytes32[](
initCampaign.socialKPIs.kpis.length
);
while (i < initCampaign.socialKPIs.kpis.length) {
totalPcts += initCampaign.socialKPIs.kpis[i].pct;
// Concatenate the social network and KPI method.
kpis[i] = keccak256(
abi.encodePacked(
string.concat(
initCampaign.socialKPIs.social,
": ",
initCampaign.socialKPIs.kpis[i].method
)
)
);
// Ensure the KPI has a valid predefined ratio in the protocol.
(, uint256 socialKPIratio, , ) = IEchoAdministration(ECHO_ADMIN)
.modelData(address(0), kpis[i]);
if (socialKPIratio == 0)
revert SOCIAL_KPI_RATIO_IS_ZERO(
string.concat(
initCampaign.socialKPIs.social,
": ",
initCampaign.socialKPIs.kpis[i].method
)
);
initCampaign.socialKPIs.kpis[i].ratio = socialKPIratio;
unchecked {
i++;
}
}
// Ensure the sum of KPI percentages is exactly 100%.
if (totalPcts != 100)
revert SUM_OF_SOCIAL_KPI_PARAMS_MUST_BE_100(totalPcts);
// Ensure the provided social network and KPIs are allowed.
if (
!IEchoAdministration(ECHO_ADMIN).allowedActionKind(address(0), kpis)
) revert ONLY_ALLOWED_SOCIAL_KPIS();
// Clone the campaign contract and deploy it.
clonedCampaign = ClonesWithImmutableArgs.clone3(
implementation,
abi.encodePacked(
ECHO_ADMIN,
address(this),
owner,
initCampaign.refundAddress,
uint256(keccak256(bytes(name))),
initCampaign.startTime,
initCampaign.endTime
),
keccak256(bytes(name))
);
// Calculate the protocol fee based on the campaign's reserved budget.
uint128 fee = (initCampaign.budgetInfo.reservedAmount *
IEchoAdministration(ECHO_ADMIN).campaignFeeRate()) / 1e5;
// Deduct the protocol fee from the reserved budget.
initCampaign.budgetInfo.reservedAmount -= fee;
// Initialize the cloned campaign with the provided parameters.
IEchoCampaign(clonedCampaign).init(
initCampaign.ipfsCID,
initCampaign.budgetInfo,
initCampaign.socialKPIs,
initCampaign.qaData
);
// Transfer the protocol fee to the protocol admin.
IERC20(initCampaign.budgetInfo.token).transferFrom(
msg.sender,
_protocolAdmin(),
fee
);
// Transfer the remaining reserved budget to the newly deployed campaign.
IERC20(initCampaign.budgetInfo.token).transferFrom(
msg.sender,
clonedCampaign,
initCampaign.budgetInfo.reservedAmount
);
// Store campaign initialization data.
_campaignNameToInitData[name] = CampaignInitData(
uint64(block.timestamp),
clonedCampaign,
implementation
);
campaignNameHashToName[keccak256(bytes(name))] = name;
allCampaigns.push(clonedCampaign);
// Emit an event indicating the successful creation of the campaign.
emit CampaignCreated(
clonedCampaign,
owner,
implementation,
name,
IEchoCampaign(clonedCampaign).applicationFee(),
"EchoMarketData",
allCampaigns.length,
initCampaign
);
}
/*****************************\
|-*-*-*-* GETTERS *-*-*-*-|
\*****************************/
/// @inheritdoc IEchoCampaignFactory
function paginatedCampaigns(uint256 page)
external
view
returns (
uint256 currentPage,
uint256 totalPages,
address[] memory pagedArray
)
{
if (allCampaigns.length == 0)
return (currentPage, totalPages, pagedArray);
else if (allCampaigns.length < 11) {
pagedArray = new address[](allCampaigns.length);
uint256 x;
while (true) {
pagedArray[x] = allCampaigns[allCampaigns.length - 1 - x];
if (x == allCampaigns.length - 1) break;
unchecked {
x++;
}
}
return (1, 1, pagedArray);
}
if (page == 0) page = 1;
totalPages = allCampaigns.length / 10;
uint256 diffLength = allCampaigns.length - (totalPages * 10);
if (totalPages * 10 < allCampaigns.length) totalPages++;
if (page > totalPages) page = totalPages;
currentPage = page;
uint256 firstIndex;
uint256 lastIndex;
if (page == 1) {
firstIndex = allCampaigns.length - 1;
lastIndex = firstIndex - 10;
} else if (page == totalPages)
firstIndex = diffLength == 0 ? firstIndex = 9 : diffLength - 1;
else {
firstIndex +=
((totalPages - page) * 10) +
(diffLength != 0 ? diffLength - 1 : 0);
lastIndex +=
((totalPages - page - 1) * 10) +
(diffLength != 0 ? diffLength - 1 : 0);
}
pagedArray = new address[]((firstIndex + 1) - lastIndex);
uint256 i;
while (true) {
pagedArray[i] = allCampaigns[firstIndex];
if (firstIndex == lastIndex) break;
unchecked {
i++;
firstIndex--;
}
}
}
/// @inheritdoc IEchoCampaignFactory
function campaignNameToInitData(string calldata name)
public
view
returns (CampaignInitData memory)
{
if (_campaignNameToInitData[name].clonedCampaign == address(0))
return ECHO_FACTORY_V1.campaignNameToInitData(name);
return _campaignNameToInitData[name];
}
/*****************************\
|-*-*-*-* PRIVATE *-*-*-*-|
\*****************************/
function _protocolAdmin() private view returns (address) {
return IEchoAdministration(ECHO_ADMIN).protocolAdmin();
}
function _revertZeroAddress(address addr) private pure {
if (addr == address(0)) revert ZERO_ADDRESS_PROVIDED();
}
function _onlyWhitelistedToken(address token) private view {
if (!IEchoAdministration(ECHO_ADMIN).whitelistedToken(token))
revert ONLY_WHITELISTED_TOKEN(token);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
// ███████╗ ██████╗██╗ ██╗ ██████╗ ███╗ ███╗ █████╗ ██████╗ ██╗ ██╗███████╗████████╗
// ██╔════╝██╔════╝██║ ██║██╔═══██╗ ████╗ ████║██╔══██╗██╔══██╗██║ ██╔╝██╔════╝╚══██╔══╝
// █████╗ ██║ ███████║██║ ██║ ██╔████╔██║███████║██████╔╝█████╔╝ █████╗ ██║
// ██╔══╝ ██║ ██╔══██║██║ ██║ ██║╚██╔╝██║██╔══██║██╔══██╗██╔═██╗ ██╔══╝ ██║
// ███████╗╚██████╗██║ ██║╚██████╔╝ ██║ ╚═╝ ██║██║ ██║██║ ██║██║ ██╗███████╗ ██║
// ╚══════╝ ╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚═╝
// ================================= EchoContents V2 =================================
// =================================== Summer 2025 ===================================
import {ActionStatus, IEchoAdministration} from "../interfaces/IEchoAdministration.sol";
import {IEchoCampaignFactory} from "../interfaces/IEchoCampaignFactory.sol";
import {IEchoContents, ConfigOracleData, QualificationOracleData, SocialKPIsOracleData, CampaignAndContent, CampaignContentInfo, ContentDetails, ContentsData} from "../interfaces/IEchoContents.sol";
import {QA_METHOD, Status, QualificationData, SocialKPIs, IEchoCampaign} from "../interfaces/IEchoCampaign.sol";
import {UD60x18, ud, unwrap} from "@prb/math/src/UD60x18.sol";
/// @title Echo Contents contract
/// @author Dynabits.org
contract EchoContents is IEchoContents {
/********************************\
|-*-*-*-*-* STATES *-*-*-*-*-|
\********************************/
ContentsData private _contentsData;
CampaignAndContent[] public contents;
mapping(string => mapping(address => ContentDetails))
private _contentToCampaign;
mapping(address => CampaignContentInfo) private _campaignContentInfo;
/*******************************\
|-*-*-*-* CONSTANTS *-*-*-*-|
\*******************************/
IEchoContents private constant ECHO_CONTENTS_V1 =
IEchoContents(0x0DAc89c5aA805eCd1967828331Fb16576125C0ee);
IEchoAdministration public immutable ECHO_ADMIN;
IEchoCampaignFactory public immutable ECHO_FACTORY;
UD60x18 private immutable ZERO_UD60x18 = ud(0);
/********************************\
|-*-*-*-*-* EVENTS *-*-*-*-*-|
\********************************/
event ContentApplied(
address indexed campaign,
uint256 indexed lastPostTime,
uint256 indexed campaignTotalPosts,
string contentLink,
uint256 campaignMaxPerPost,
string EchoMarketData,
uint256 totalPosts
);
event ContentConfigAdded(
address indexed campaign,
address indexed oracle,
string contentLink,
ConfigOracleData newConfig
);
event ContentQAoverallScore(
address indexed campaign,
string contentLink,
uint256 indexed QAoverallScore
);
event NewQualificationSettled(
address indexed campaign,
address indexed oracle,
string contentLink,
string qaMethodKind,
uint256 pctScore
);
event ReadyForKPIupdates(address indexed campaign, string contentLink);
event KPIupdated(
address indexed campaign,
address indexed oracle,
string contentLink,
uint256 lastCampaignUpdateTime,
string socialKindKPI,
uint256 kpiAmount
);
event ContentTotalKPIupdated(
address indexed campaign,
string contentLink,
uint256 indexed contentTotalKPI
);
event ContentEffectiveKPIupdated(
address indexed campaign,
string contentLink,
bool additionalMode,
uint256 indexed additionalContentEffectvieKPI,
uint256 campaignTotalEffectiveKPIs,
uint256 indexed contentEffectiveKPI,
uint256 campaignTotalEligibleContents,
uint256 lastUpdatedTime,
string EchoMarketData,
uint256 totalEligibleContents,
uint256 totalEffectiveKPIs
);
/********************************\
|-*-*-*-*-* ERRORS *-*-*-*-*-|
\********************************/
error CONTENT_REGISTERED_FOR_CAMPAIGN_BEFORE();
error ACCESS_DENIED();
error UNDEFINED_JOB();
error ORACLE_IS_BAN();
error ZERO_CONTENT_QA_SCORE();
error CHECK_QA_PCT(uint256 pct);
error QA_DATA_SETTLED_BEFORE(string contentLink);
error CONTENT_DOESNT_EXIST(string contentLink);
error ONLY_IN_PROGRESS_OR_PAUSED_OR_FINISHED_CAMPAIGN();
error ORACLE_DOESNT_HAVE_REQUIRED_ACCESS(string actionKind);
error GIVEN_QA_KIND_DOESNT_EXIST_ON_CAMPAIGN(string qaKind);
error GIVEN_KPI_KIND_DOESNT_EXIST_ON_CAMPAIGN(string kpiKind);
error DIFFERENT_SOCIALS();
/******************************\
|-*-*-*-* BUILT-IN *-*-*-*-|
\******************************/
constructor(address echoAdministration) {
ECHO_ADMIN = IEchoAdministration(echoAdministration);
ECHO_FACTORY = IEchoCampaignFactory(msg.sender);
}
/*******************************\
|*-*-*-* EXTERNALS *-*-*-*-*|
\*******************************/
/// @inheritdoc IEchoContents
function addContentLink(
string calldata contentLink,
uint256 campaignMaxPerPost
) external {
// Ensure that the caller is a registered campaign.
if (
ECHO_FACTORY
.campaignNameToInitData(IEchoCampaign(msg.sender).name())
.clonedCampaign == address(0)
) revert ACCESS_DENIED();
// Check if the content has already been registered for this campaign.
if (_contentToCampaign[contentLink][msg.sender].contentRegistered)
revert CONTENT_REGISTERED_FOR_CAMPAIGN_BEFORE();
// Store the content reference associated with the calling campaign.
contents.push(CampaignAndContent(msg.sender, contentLink));
_contentToCampaign[contentLink][msg.sender].contentRegistered = true;
// Update the campaign's content records.
_campaignContentInfo[msg.sender].contents.push(contentLink);
_campaignContentInfo[msg.sender].lastPostTime = uint128(
block.timestamp
);
_contentsData.totalPosts++;
// Emit an event indicating that new content has been applied to the campaign.
emit ContentApplied(
msg.sender,
block.timestamp,
_campaignContentInfo[msg.sender].contents.length,
contentLink,
campaignMaxPerPost,
"EchoMarketData",
_contentsData.totalPosts
);
}
/// @inheritdoc IEchoContents
function setContentData(
address campaign,
string calldata contentLink,
ConfigOracleData[] calldata configs,
QualificationOracleData[] calldata qualifications
) external {
// Ensure that at least one configuration or qualification is provided.
if (configs.length == 0 && qualifications.length == 0)
revert UNDEFINED_JOB();
bool isProtocolAdmin = msg.sender == ECHO_ADMIN.protocolAdmin();
// If the caller is not the protocol admin, verify that they are not a banned oracle.
if (ECHO_ADMIN.disallowedOracle(msg.sender) && !isProtocolAdmin)
revert ORACLE_IS_BAN();
// Verify that the specified content has been registered within the campaign.
if (!_contentToCampaign[contentLink][campaign].contentRegistered)
revert CONTENT_DOESNT_EXIST(contentLink);
string memory actionKind;
// Process and register configuration data.
if (configs.length != 0) {
address oracle;
uint256 i;
while (i < configs.length) {
actionKind = string.concat("CONFIG: ", configs[i].kind);
// Ensure the caller has the required oracle permissions for configuration.
(, , , oracle) = ECHO_ADMIN.modelData(
address(1),
keccak256(abi.encodePacked(actionKind))
);
if (oracle != msg.sender && !isProtocolAdmin)
revert ORACLE_DOESNT_HAVE_REQUIRED_ACCESS(actionKind);
// Store the configuration value for the content.
_contentToCampaign[contentLink][campaign].actionKindToValue[
actionKind
] = uint256(bytes32(configs[i].value));
emit ContentConfigAdded(
campaign,
msg.sender,
contentLink,
configs[i]
);
unchecked {
i++;
}
}
}
// Process and register qualification data.
if (qualifications.length != 0) {
uint256 campaignTotalQas = IEchoCampaign(campaign).qaData().length;
// If the caller is not the protocol admin, ensure they can only set remaining qualifications.
if (
campaignTotalQas -
_contentToCampaign[contentLink][campaign].totalQaKinds !=
qualifications.length &&
!isProtocolAdmin
) revert QA_DATA_SETTLED_BEFORE(contentLink);
ActionStatus actionStat;
uint256 i;
while (i < qualifications.length) {
// Define the action kind based on the qualification method.
if (qualifications[i].method == QA_METHOD.AI)
actionKind = string.concat(
"QA-AI: ",
qualifications[i].kind
);
else if (qualifications[i].method == QA_METHOD.COMMUNITY)
actionKind = string.concat(
"QA-C: ",
qualifications[i].kind
);
else
actionKind = string.concat(
"QA-V: ",
qualifications[i].kind
);
// Ensure that the qualification kind is valid within the campaign.
if (!IEchoCampaign(campaign).registeredActionKind(actionKind))
revert GIVEN_QA_KIND_DOESNT_EXIST_ON_CAMPAIGN(actionKind);
// Ensure the caller has the required oracle permissions for qualification.
(actionStat, , , ) = ECHO_ADMIN.modelData(
msg.sender,
keccak256(abi.encodePacked(actionKind))
);
if (actionStat != ActionStatus.QA && !isProtocolAdmin)
revert ORACLE_DOESNT_HAVE_REQUIRED_ACCESS(actionKind);
// Ensure the qualification percentage is within the valid range (0 < pct ≤ 1e5).
if (qualifications[i].pct > 1e5 || qualifications[i].pct == 0)
revert CHECK_QA_PCT(qualifications[i].pct);
// Ensure that if the caller is not the protocol admin, they are not setting a qualification again.
if (
_contentToCampaign[contentLink][campaign].actionKindToValue[
actionKind
] != 0
) {
if (!isProtocolAdmin)
revert QA_DATA_SETTLED_BEFORE(contentLink);
} else {
_contentToCampaign[contentLink][campaign].totalQaKinds++;
}
// Store the qualification percentage for the content.
_contentToCampaign[contentLink][campaign].actionKindToValue[
actionKind
] = qualifications[i].pct;
emit NewQualificationSettled(
campaign,
msg.sender,
contentLink,
actionKind,
qualifications[i].pct
);
unchecked {
i++;
}
}
// If all required qualifications are set, compute the overall qualification score.
if (
_contentToCampaign[contentLink][campaign].totalQaKinds ==
campaignTotalQas
) {
UD60x18 overallScore = ud(1e18);
QualificationData[] memory qaData = IEchoCampaign(campaign)
.qaData();
string memory ANTI_DEEPSTACK_CL = contentLink;
address ANTI_DEEPSTACK_C = campaign;
delete i;
// Compute the overall qualification score using the predefined formula.
while (i < qaData.length) {
// Define the action kind based on the qualification method.
if (qaData[i].method == QA_METHOD.AI)
actionKind = string.concat("QA-AI: ", qaData[i].kind);
else if (qaData[i].method == QA_METHOD.COMMUNITY)
actionKind = string.concat("QA-C: ", qaData[i].kind);
else actionKind = string.concat("QA-V: ", qaData[i].kind);
overallScore = overallScore.mul(
ud(
_contentToCampaign[ANTI_DEEPSTACK_CL][
ANTI_DEEPSTACK_C
].actionKindToValue[actionKind] * 1e13
).pow(ud(qaData[i].pct * 1e16))
);
// If any qualification score is zero, the final result is zero.
if (overallScore == ZERO_UD60x18) break;
unchecked {
i++;
}
}
// Store the overall qualification score for the content.
_contentToCampaign[ANTI_DEEPSTACK_CL][ANTI_DEEPSTACK_C]
.contentOverallQaScore = overallScore;
emit ContentQAoverallScore(
ANTI_DEEPSTACK_C,
ANTI_DEEPSTACK_CL,
unwrap(overallScore)
);
}
// If the overall QA score is set, emit an event to indicate readiness for KPI updates.
if (
_contentToCampaign[contentLink][campaign]
.contentOverallQaScore != ZERO_UD60x18
) emit ReadyForKPIupdates(campaign, contentLink);
}
/*
EDGE CASE: If Effective KPI already exists for a content and the protocol invokes the function
again, obtaining a new Effective KPI requires re-invoking the updateKPIs function.
However, due to high gas costs and potential multiple loops
the update is not performed automatically.
*/
}
/// @inheritdoc IEchoContents
function updateKPIs(
address campaign,
string calldata contentLink,
SocialKPIsOracleData calldata socialKPIs
) external {
bool isProtocolAdmin = msg.sender == ECHO_ADMIN.protocolAdmin();
// Ensure the caller is either the protocol admin or a permitted oracle.
// If the caller is a banned oracle and not the admin, revert.
if (ECHO_ADMIN.disallowedOracle(msg.sender) && !isProtocolAdmin)
revert ORACLE_IS_BAN();
// Verify if the content exists for the given campaign.
// If not, revert with `CONTENT_DOESNT_EXIST`.
if (!_contentToCampaign[contentLink][campaign].contentRegistered)
revert CONTENT_DOESNT_EXIST(contentLink);
// Ensure that if the caller is not the protocol admin, the campaign must be in
// a valid status: InProgress, Paused, or Finished.
Status stat = IEchoCampaign(campaign).currentStatus();
if (
(stat != Status.inProgress &&
stat != Status.paused &&
stat != Status.finished) && !isProtocolAdmin
) revert ONLY_IN_PROGRESS_OR_PAUSED_OR_FINISHED_CAMPAIGN();
// Check if the content has a valid Quality Assurance (QA) score.
// If the score is zero, revert.
if (
_contentToCampaign[contentLink][campaign]
.contentOverallQaScore
.unwrap() == 0
) revert ZERO_CONTENT_QA_SCORE();
// Retrieve the campaign’s social KPIs and validate them.
SocialKPIs memory campaignSocialKPIs = IEchoCampaign(campaign)
.socialKPIs();
string memory kpiAction = socialKPIs.social;
// Ensure that the social KPI being updated matches the campaign's selected KPI.
if (
keccak256(abi.encodePacked(campaignSocialKPIs.social)) !=
keccak256(abi.encodePacked(kpiAction))
) revert DIFFERENT_SOCIALS();
uint256 i;
string memory actionKind;
address oracle;
while (i < socialKPIs.kpis.length) {
// Construct the KPI action kind string.
actionKind = string.concat(
kpiAction,
": ",
socialKPIs.kpis[i].method
);
// Ensure the KPI action is registered in the campaign.
if (!IEchoCampaign(campaign).registeredActionKind(actionKind))
revert GIVEN_KPI_KIND_DOESNT_EXIST_ON_CAMPAIGN(actionKind);
// Verify that the caller is an authorized oracle with permissions to update KPIs.
(, , , oracle) = ECHO_ADMIN.modelData(
address(0),
keccak256(abi.encodePacked(actionKind))
);
if (msg.sender != oracle && !isProtocolAdmin)
revert ORACLE_DOESNT_HAVE_REQUIRED_ACCESS(actionKind);
// Update the KPI value for the given content and campaign.
_contentToCampaign[contentLink][campaign].actionKindToValue[
actionKind
] = socialKPIs.kpis[i].value;
// Emit event to indicate one KPI updated.
emit KPIupdated(
campaign,
msg.sender,
contentLink,
block.timestamp,
actionKind,
socialKPIs.kpis[i].value
);
unchecked {
i++;
}
}
_campaignContentInfo[campaign].lastUpdatedTime = uint128(
block.timestamp
);
delete i;
UD60x18 totalKPI = ud(0);
string memory ANTI_DEEPSTACK_CL = contentLink;
address ANTI_DEEPSTACK_C = campaign;
while (i < campaignSocialKPIs.kpis.length) {
actionKind = string.concat(
kpiAction,
": ",
campaignSocialKPIs.kpis[i].method
);
// Ensure KPI values meet the campaign’s minimum requirements.
if (
campaignSocialKPIs.kpis[i].min >
_contentToCampaign[ANTI_DEEPSTACK_CL][ANTI_DEEPSTACK_C]
.actionKindToValue[actionKind]
) {
totalKPI = ZERO_UD60x18;
break;
}
// Compute the overall KPI formula based on campaign settings.
totalKPI = totalKPI.add(
ud(uint256(campaignSocialKPIs.kpis[i].pct) * 1e16).mul(
ud(
_contentToCampaign[ANTI_DEEPSTACK_CL][ANTI_DEEPSTACK_C]
.actionKindToValue[actionKind]
).div(ud(campaignSocialKPIs.kpis[i].ratio))
)
);
unchecked {
i++;
}
}
emit ContentTotalKPIupdated(
ANTI_DEEPSTACK_C,
ANTI_DEEPSTACK_CL,
totalKPI.unwrap()
);
// Calculate the Effective KPI impact based on campaign rules.
(
UD60x18 contentEffectiveKPI,
UD60x18 plusMinusContentEffectiveKPI,
bool isAdditional
) = (
ud(
IEchoCampaign(ANTI_DEEPSTACK_C).contentEffectiveKPI(
ANTI_DEEPSTACK_CL
)
),
ud(0),
true
);
// Determine whether the KPI change is an addition or subtraction.
if (contentEffectiveKPI != ZERO_UD60x18 && totalKPI == ZERO_UD60x18)
delete isAdditional;
else if (
contentEffectiveKPI == ZERO_UD60x18 && totalKPI != ZERO_UD60x18
)
plusMinusContentEffectiveKPI = (
totalKPI.mul(
_contentToCampaign[ANTI_DEEPSTACK_CL][ANTI_DEEPSTACK_C]
.contentOverallQaScore
)
);
else if (
contentEffectiveKPI != ZERO_UD60x18 && totalKPI != ZERO_UD60x18
) {
plusMinusContentEffectiveKPI = (
totalKPI.mul(
_contentToCampaign[ANTI_DEEPSTACK_CL][ANTI_DEEPSTACK_C]
.contentOverallQaScore
)
);
if (plusMinusContentEffectiveKPI > contentEffectiveKPI)
plusMinusContentEffectiveKPI = plusMinusContentEffectiveKPI.sub(
contentEffectiveKPI
);
else {
plusMinusContentEffectiveKPI = contentEffectiveKPI.sub(
plusMinusContentEffectiveKPI
);
delete isAdditional;
}
}
if (plusMinusContentEffectiveKPI != ZERO_UD60x18) {
uint256 _campaignTotalEffectiveKPIs;
uint256 _contentEffectiveKPI;
uint256 _campaignTotalEligibleContents;
int8 eligibilityPolarity;
// Update the total and individual Effective KPI values within the campaign.
(
_campaignTotalEffectiveKPIs,
_contentEffectiveKPI,
_campaignTotalEligibleContents,
eligibilityPolarity
) = IEchoCampaign(ANTI_DEEPSTACK_C).updateKPIs(
ANTI_DEEPSTACK_CL,
plusMinusContentEffectiveKPI.unwrap(),
isAdditional
);
_contentToCampaign[ANTI_DEEPSTACK_CL][ANTI_DEEPSTACK_C]
.lastUpdatedTime = uint240(block.timestamp);
if (eligibilityPolarity != 0)
eligibilityPolarity == 1
? _contentsData.totalEligibleContents++
: _contentsData.totalEligibleContents--;
isAdditional
? _contentsData
.totalEffectiveKPIs += plusMinusContentEffectiveKPI.unwrap()
: _contentsData
.totalEffectiveKPIs -= plusMinusContentEffectiveKPI
.unwrap();
// Emit event for KPI updates at the campaign level.
emit ContentEffectiveKPIupdated(
ANTI_DEEPSTACK_C,
ANTI_DEEPSTACK_CL,
isAdditional,
plusMinusContentEffectiveKPI.unwrap(),
_campaignTotalEffectiveKPIs,
_contentEffectiveKPI,
_campaignTotalEligibleContents,
block.timestamp,
"EchoMarketData",
_contentsData.totalEligibleContents,
_contentsData.totalEffectiveKPIs
);
}
}
/*****************************\
|-*-*-*-* GETTERS *-*-*-*-|
\*****************************/
/// @inheritdoc IEchoContents
function campaignContentInfo(address campaign)
external
view
returns (uint256 lastPostTime, uint256 lastUpdatedTime)
{
(lastPostTime, lastUpdatedTime) = (
_campaignContentInfo[campaign].lastPostTime,
_campaignContentInfo[campaign].lastUpdatedTime
);
if (lastPostTime == 0 && lastUpdatedTime == 0)
(lastPostTime, lastUpdatedTime) = ECHO_CONTENTS_V1
.campaignContentInfo(campaign);
}
/// @inheritdoc IEchoContents
function contentToCampaign(
address campaign,
string calldata contentLink,
string calldata actionKind
)
external
view
returns (
bool contentRegistered,
uint256 totalQaKinds,
uint256 lastUpdatedTime,
uint256 actionKindValue
)
{
contentRegistered = _contentToCampaign[contentLink][campaign]
.contentRegistered;
if (contentRegistered) {
totalQaKinds = _contentToCampaign[contentLink][campaign]
.totalQaKinds;
lastUpdatedTime = _contentToCampaign[contentLink][campaign]
.lastUpdatedTime;
if (bytes(actionKind).length != 0)
actionKindValue = _contentToCampaign[contentLink][campaign]
.actionKindToValue[actionKind];
} else {
(
contentRegistered,
totalQaKinds,
lastUpdatedTime,
actionKindValue
) = ECHO_CONTENTS_V1.contentToCampaign(
campaign,
contentLink,
actionKind
);
}
}
/// @inheritdoc IEchoContents
function paginatedContentsWithCampaigns(uint256 page)
external
view
returns (
uint256 currentPage,
uint256 totalPages,
CampaignAndContent[] memory pagedArray
)
{
if (contents.length == 0) return (currentPage, totalPages, pagedArray);
else if (contents.length < 11) {
pagedArray = new CampaignAndContent[](contents.length);
uint256 x;
while (true) {
pagedArray[x] = contents[contents.length - 1 - x];
if (x == contents.length - 1) break;
unchecked {
x++;
}
}
return (1, 1, pagedArray);
}
if (page == 0) page = 1;
totalPages = contents.length / 10;
uint256 diffLength = contents.length - (totalPages * 10);
if (totalPages * 10 < contents.length) totalPages++;
if (page > totalPages) page = totalPages;
currentPage = page;
uint256 firstIndex;
uint256 lastIndex;
if (page == 1) {
firstIndex = contents.length - 1;
lastIndex = firstIndex - 10;
} else if (page == totalPages)
firstIndex = diffLength == 0 ? firstIndex = 9 : diffLength - 1;
else {
firstIndex +=
((totalPages - page) * 10) +
(diffLength != 0 ? diffLength - 1 : 0);
lastIndex +=
((totalPages - page - 1) * 10) +
(diffLength != 0 ? diffLength - 1 : 0);
}
pagedArray = new CampaignAndContent[]((firstIndex + 1) - lastIndex);
uint256 i;
while (true) {
pagedArray[i] = contents[firstIndex];
if (firstIndex == lastIndex) break;
unchecked {
i++;
firstIndex--;
}
}
}
/// @inheritdoc IEchoContents
function paginatedCampaignContents(uint256 page, address campaign)
external
view
returns (
uint256 currentPage,
uint256 totalPages,
string[] memory pagedArray
)
{
if (_campaignContentInfo[campaign].contents.length == 0)
return ECHO_CONTENTS_V1.paginatedCampaignContents(page, campaign);
else if (_campaignContentInfo[campaign].contents.length < 11) {
pagedArray = new string[](
_campaignContentInfo[campaign].contents.length
);
uint256 x;
while (true) {
pagedArray[x] = _campaignContentInfo[campaign].contents[
_campaignContentInfo[campaign].contents.length - 1 - x
];
if (x == _campaignContentInfo[campaign].contents.length - 1)
break;
unchecked {
x++;
}
}
return (1, 1, pagedArray);
}
if (page == 0) page = 1;
totalPages = _campaignContentInfo[campaign].contents.length / 10;
uint256 diffLength = _campaignContentInfo[campaign].contents.length -
(totalPages * 10);
if (totalPages * 10 < _campaignContentInfo[campaign].contents.length)
totalPages++;
if (page > totalPages) page = totalPages;
currentPage = page;
uint256 firstIndex;
uint256 lastIndex;
if (page == 1) {
firstIndex = _campaignContentInfo[campaign].contents.length - 1;
lastIndex = firstIndex - 10;
} else if (page == totalPages)
firstIndex = diffLength == 0 ? firstIndex = 9 : diffLength - 1;
else {
firstIndex +=
((totalPages - page) * 10) +
(diffLength != 0 ? diffLength - 1 : 0);
lastIndex +=
((totalPages - page - 1) * 10) +
(diffLength != 0 ? diffLength - 1 : 0);
}
pagedArray = new string[]((firstIndex + 1) - lastIndex);
uint256 i;
while (true) {
pagedArray[i] = _campaignContentInfo[campaign].contents[firstIndex];
if (firstIndex == lastIndex) break;
unchecked {
i++;
firstIndex--;
}
}
}
/**
* @notice Migrated version of `paginatedContentsWithCampaigns`.
* @dev This function provides both the original (V1) and the migrated (V2)
* paginated contents with campaigns in a single call. It is useful for
* backward compatibility while transitioning to the new data structure.
*
* @param page The page number to fetch.
*
* @return currentPageV1 The current page number from the original (V1) logic.
* @return totalPagesV1 The total number of pages from the original (V1) logic.
* @return pagedArrayV1 The paginated contents associated with campaigns from V1.
* @return currentPageV2 The current page number from the migrated (V2) logic.
* @return totalPagesV2 The total number of pages from the migrated (V2) logic.
* @return pagedArrayV2 The paginated contents associated with campaigns from V2.
*/
function migratedPaginatedContentsWithCampaigns(uint256 page)
external
view
returns (
uint256 currentPageV1,
uint256 totalPagesV1,
CampaignAndContent[] memory pagedArrayV1,
uint256 currentPageV2,
uint256 totalPagesV2,
CampaignAndContent[] memory pagedArrayV2
)
{
(currentPageV1, totalPagesV1, pagedArrayV1) = ECHO_CONTENTS_V1
.paginatedContentsWithCampaigns(page);
if (contents.length == 0)
return (
currentPageV1,
totalPagesV1,
pagedArrayV1,
currentPageV2,
totalPagesV2,
pagedArrayV2
);
else if (contents.length < 11) {
pagedArrayV2 = new CampaignAndContent[](contents.length);
uint256 x;
while (true) {
pagedArrayV2[x] = contents[contents.length - 1 - x];
if (x == contents.length - 1) break;
unchecked {
x++;
}
}
return (
currentPageV1,
totalPagesV1,
pagedArrayV1,
1,
1,
pagedArrayV2
);
}
if (page == 0) page = 1;
totalPagesV2 = contents.length / 10;
uint256 diffLength = contents.length - (totalPagesV2 * 10);
if (totalPagesV2 * 10 < contents.length) totalPagesV2++;
if (page > totalPagesV2) page = totalPagesV2;
currentPageV2 = page;
uint256 firstIndex;
uint256 lastIndex;
if (page == 1) {
firstIndex = contents.length - 1;
lastIndex = firstIndex - 10;
} else if (page == totalPagesV2)
firstIndex = diffLength == 0 ? firstIndex = 9 : diffLength - 1;
else {
firstIndex +=
((totalPagesV2 - page) * 10) +
(diffLength != 0 ? diffLength - 1 : 0);
lastIndex +=
((totalPagesV2 - page - 1) * 10) +
(diffLength != 0 ? diffLength - 1 : 0);
}
pagedArrayV2 = new CampaignAndContent[]((firstIndex + 1) - lastIndex);
uint256 i;
while (true) {
pagedArrayV2[i] = contents[firstIndex];
if (firstIndex == lastIndex) break;
unchecked {
i++;
firstIndex--;
}
}
}
}// SPDX-License-Identifier: BSD
pragma solidity 0.8.30;
/// @title ClonesWithImmutableArgs
/// @author wighawag, zefram.eth, nick.eth
/// @notice Enables creating clone contracts with immutable args
library ClonesWithImmutableArgs {
/// @dev The CREATE3 proxy bytecode.
uint256 private constant _CREATE3_PROXY_BYTECODE =
0x67363d3d37363d34f03d5260086018f3;
/// @dev Hash of the `_CREATE3_PROXY_BYTECODE`.
/// Equivalent to `keccak256(abi.encodePacked(hex"67363d3d37363d34f03d5260086018f3"))`.
bytes32 private constant _CREATE3_PROXY_BYTECODE_HASH =
0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f;
error CreateFail();
error InitializeFail();
enum CloneType {
CREATE,
CREATE2,
PREDICT_CREATE2
}
/// @notice Creates a clone proxy of the implementation contract, with immutable args
/// @dev data cannot exceed 65535 bytes, since 2 bytes are used to store the data length
/// @param implementation The implementation contract to clone
/// @param data Encoded immutable args
/// @return instance The address of the created clone
function clone(
address implementation,
bytes memory data
) internal returns (address payable instance) {
return clone(implementation, data, 0);
}
/// @notice Creates a clone proxy of the implementation contract, with immutable args
/// @dev data cannot exceed 65535 bytes, since 2 bytes are used to store the data length
/// @param implementation The implementation contract to clone
/// @param data Encoded immutable args
/// @param value The amount of wei to transfer to the created clone
/// @return instance The address of the created clone
function clone(
address implementation,
bytes memory data,
uint256 value
) internal returns (address payable instance) {
bytes memory creationcode = getCreationBytecode(implementation, data);
// solhint-disable-next-line no-inline-assembly
assembly {
instance := create(
value,
add(creationcode, 0x20),
mload(creationcode)
)
}
if (instance == address(0)) {
revert CreateFail();
}
}
/// @notice Creates a clone proxy of the implementation contract, with immutable args,
/// using CREATE2
/// @dev data cannot exceed 65535 bytes, since 2 bytes are used to store the data length
/// @param implementation The implementation contract to clone
/// @param data Encoded immutable args
/// @return instance The address of the created clone
function clone2(
address implementation,
bytes memory data
) internal returns (address payable instance) {
return clone2(implementation, data, 0);
}
/// @notice Creates a clone proxy of the implementation contract, with immutable args,
/// using CREATE2
/// @dev data cannot exceed 65535 bytes, since 2 bytes are used to store the data length
/// @param implementation The implementation contract to clone
/// @param data Encoded immutable args
/// @param value The amount of wei to transfer to the created clone
/// @return instance The address of the created clone
function clone2(
address implementation,
bytes memory data,
uint256 value
) internal returns (address payable instance) {
bytes memory creationcode = getCreationBytecode(implementation, data);
// solhint-disable-next-line no-inline-assembly
assembly {
instance := create2(
value,
add(creationcode, 0x20),
mload(creationcode),
0
)
}
if (instance == address(0)) {
revert CreateFail();
}
}
/// @notice Computes the address of a clone created using CREATE2
/// @dev data cannot exceed 65535 bytes, since 2 bytes are used to store the data length
/// @param implementation The implementation contract to clone
/// @param data Encoded immutable args
/// @return instance The address of the clone
function addressOfClone2(
address implementation,
bytes memory data
) internal view returns (address payable instance) {
bytes memory creationcode = getCreationBytecode(implementation, data);
bytes32 bytecodeHash = keccak256(creationcode);
instance = payable(
address(
uint160(
uint256(
keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
bytes32(0),
bytecodeHash
)
)
)
)
)
);
}
/// @notice Computes bytecode for a clone
/// @dev data cannot exceed 65535 bytes, since 2 bytes are used to store the data length
/// @param implementation The implementation contract to clone
/// @param data Encoded immutable args
/// @return ret Creation bytecode for the clone contract
function getCreationBytecode(
address implementation,
bytes memory data
) internal pure returns (bytes memory ret) {
// unrealistic for memory ptr or data length to exceed 256 bits
unchecked {
uint256 extraLength = data.length + 2; // +2 bytes for telling how much data there is appended to the call
uint256 creationSize = 0x41 + extraLength;
uint256 runSize = creationSize - 10;
uint256 dataPtr;
uint256 ptr;
// solhint-disable-next-line no-inline-assembly
assembly {
ret := mload(0x40)
mstore(ret, creationSize)
mstore(0x40, add(ret, creationSize))
ptr := add(ret, 0x20)
// -------------------------------------------------------------------------------------------------------------
// CREATION (10 bytes)
// -------------------------------------------------------------------------------------------------------------
// 61 runtime | PUSH2 runtime (r) | r | –
mstore(
ptr,
0x6100000000000000000000000000000000000000000000000000000000000000
)
mstore(add(ptr, 0x01), shl(240, runSize)) // size of the contract running bytecode (16 bits)
// creation size = 0a
// 3d | RETURNDATASIZE | 0 r | –
// 81 | DUP2 | r 0 r | –
// 60 creation | PUSH1 creation (c) | c r 0 r | –
// 3d | RETURNDATASIZE | 0 c r 0 r | –
// 39 | CODECOPY | 0 r | [0-runSize): runtime code
// f3 | RETURN | | [0-runSize): runtime code
// -------------------------------------------------------------------------------------------------------------
// RUNTIME (55 bytes + extraLength)
// -------------------------------------------------------------------------------------------------------------
// 3d | RETURNDATASIZE | 0 | –
// 3d | RETURNDATASIZE | 0 0 | –
// 3d | RETURNDATASIZE | 0 0 0 | –
// 3d | RETURNDATASIZE | 0 0 0 0 | –
// 36 | CALLDATASIZE | cds 0 0 0 0 | –
// 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | –
// 3d | RETURNDATASIZE | 0 0 cds 0 0 0 0 | –
// 37 | CALLDATACOPY | 0 0 0 0 | [0, cds) = calldata
// 61 | PUSH2 extra | extra 0 0 0 0 | [0, cds) = calldata
mstore(
add(ptr, 0x03),
0x3d81600a3d39f33d3d3d3d363d3d376100000000000000000000000000000000
)
mstore(add(ptr, 0x13), shl(240, extraLength))
// 60 0x37 | PUSH1 0x37 | 0x37 extra 0 0 0 0 | [0, cds) = calldata // 0x37 (55) is runtime size - data
// 36 | CALLDATASIZE | cds 0x37 extra 0 0 0 0 | [0, cds) = calldata
// 39 | CODECOPY | 0 0 0 0 | [0, cds) = calldata, [cds, cds+extra) = extraData
// 36 | CALLDATASIZE | cds 0 0 0 0 | [0, cds) = calldata, [cds, cds+extra) = extraData
// 61 extra | PUSH2 extra | extra cds 0 0 0 0 | [0, cds) = calldata, [cds, cds+extra) = extraData
mstore(
add(ptr, 0x15),
0x6037363936610000000000000000000000000000000000000000000000000000
)
mstore(add(ptr, 0x1b), shl(240, extraLength))
// 01 | ADD | cds+extra 0 0 0 0 | [0, cds) = calldata, [cds, cds+extra) = extraData
// 3d | RETURNDATASIZE | 0 cds+extra 0 0 0 0 | [0, cds) = calldata, [cds, cds+extra) = extraData
// 73 addr | PUSH20 0x123… | addr 0 cds+extra 0 0 0 0 | [0, cds) = calldata, [cds, cds+extra) = extraData
mstore(
add(ptr, 0x1d),
0x013d730000000000000000000000000000000000000000000000000000000000
)
mstore(add(ptr, 0x20), shl(0x60, implementation))
// 5a | GAS | gas addr 0 cds+extra 0 0 0 0 | [0, cds) = calldata, [cds, cds+extra) = extraData
// f4 | DELEGATECALL | success 0 0 | [0, cds) = calldata, [cds, cds+extra) = extraData
// 3d | RETURNDATASIZE | rds success 0 0 | [0, cds) = calldata, [cds, cds+extra) = extraData
// 3d | RETURNDATASIZE | rds rds success 0 0 | [0, cds) = calldata, [cds, cds+extra) = extraData
// 93 | SWAP4 | 0 rds success 0 rds | [0, cds) = calldata, [cds, cds+extra) = extraData
// 80 | DUP1 | 0 0 rds success 0 rds | [0, cds) = calldata, [cds, cds+extra) = extraData
// 3e | RETURNDATACOPY | success 0 rds | [0, rds) = return data (there might be some irrelevant leftovers in memory [rds, cds+0x37) when rds < cds+0x37)
// 60 0x35 | PUSH1 0x35 | 0x35 sucess 0 rds | [0, rds) = return data
// 57 | JUMPI | 0 rds | [0, rds) = return data
// fd | REVERT | – | [0, rds) = return data
// 5b | JUMPDEST | 0 rds | [0, rds) = return data
// f3 | RETURN | – | [0, rds) = return data
mstore(
add(ptr, 0x34),
0x5af43d3d93803e603557fd5bf300000000000000000000000000000000000000
)
}
// -------------------------------------------------------------------------------------------------------------
// APPENDED DATA (Accessible from extcodecopy)
// (but also send as appended data to the delegatecall)
// -------------------------------------------------------------------------------------------------------------
extraLength -= 2;
uint256 counter = extraLength;
uint256 copyPtr = ptr + 0x41;
// solhint-disable-next-line no-inline-assembly
assembly {
dataPtr := add(data, 32)
}
for (; counter >= 32; counter -= 32) {
// solhint-disable-next-line no-inline-assembly
assembly {
mstore(copyPtr, mload(dataPtr))
}
copyPtr += 32;
dataPtr += 32;
}
uint256 mask = ~(256 ** (32 - counter) - 1);
// solhint-disable-next-line no-inline-assembly
assembly {
mstore(copyPtr, and(mload(dataPtr), mask))
}
copyPtr += counter;
// solhint-disable-next-line no-inline-assembly
assembly {
mstore(copyPtr, shl(240, extraLength))
}
}
}
/// @notice Creates a clone proxy of the implementation contract, with immutable args. Uses CREATE3
/// to implement deterministic deployment.
/// @dev data cannot exceed 65535 bytes, since 2 bytes are used to store the data length
/// @param implementation The implementation contract to clone
/// @param data Encoded immutable args
/// @param salt The salt used by the CREATE3 deployment
/// @return deployed The address of the created clone
function clone3(
address implementation,
bytes memory data,
bytes32 salt
) internal returns (address deployed) {
return clone3(implementation, data, salt, 0);
}
/// @notice Creates a clone proxy of the implementation contract, with immutable args. Uses CREATE3
/// to implement deterministic deployment.
/// @dev data cannot exceed 65535 bytes, since 2 bytes are used to store the data length
/// @param implementation The implementation contract to clone
/// @param data Encoded immutable args
/// @param salt The salt used by the CREATE3 deployment
/// @param value The amount of wei to transfer to the created clone
/// @return deployed The address of the created clone
function clone3(
address implementation,
bytes memory data,
bytes32 salt,
uint256 value
) internal returns (address deployed) {
// unrealistic for memory ptr or data length to exceed 256 bits
unchecked {
uint256 extraLength = data.length + 2; // +2 bytes for telling how much data there is appended to the call
uint256 creationSize = 0x43 + extraLength;
uint256 ptr;
// solhint-disable-next-line no-inline-assembly
assembly {
ptr := mload(0x40)
// -------------------------------------------------------------------------------------------------------------
// CREATION (11 bytes)
// -------------------------------------------------------------------------------------------------------------
// 3d | RETURNDATASIZE | 0 | –
// 61 runtime | PUSH2 runtime (r) | r 0 | –
mstore(
ptr,
0x3d61000000000000000000000000000000000000000000000000000000000000
)
mstore(add(ptr, 0x02), shl(240, sub(creationSize, 11))) // size of the contract running bytecode (16 bits)
// creation size = 0b
// 80 | DUP1 | r r 0 | –
// 60 creation | PUSH1 creation (c) | c r r 0 | –
// 3d | RETURNDATASIZE | 0 c r r 0 | –
// 39 | CODECOPY | r 0 | [0-2d]: runtime code
// 81 | DUP2 | 0 c 0 | [0-2d]: runtime code
// f3 | RETURN | 0 | [0-2d]: runtime code
mstore(
add(ptr, 0x04),
0x80600b3d3981f300000000000000000000000000000000000000000000000000
)
// -------------------------------------------------------------------------------------------------------------
// RUNTIME
// -------------------------------------------------------------------------------------------------------------
// 36 | CALLDATASIZE | cds | –
// 3d | RETURNDATASIZE | 0 cds | –
// 3d | RETURNDATASIZE | 0 0 cds | –
// 37 | CALLDATACOPY | – | [0, cds] = calldata
// 61 | PUSH2 extra | extra | [0, cds] = calldata
mstore(
add(ptr, 0x0b),
0x363d3d3761000000000000000000000000000000000000000000000000000000
)
mstore(add(ptr, 0x10), shl(240, extraLength))
// 60 0x38 | PUSH1 0x38 | 0x38 extra | [0, cds] = calldata // 0x38 (56) is runtime size - data
// 36 | CALLDATASIZE | cds 0x38 extra | [0, cds] = calldata
// 39 | CODECOPY | _ | [0, cds] = calldata
// 3d | RETURNDATASIZE | 0 | [0, cds] = calldata
// 3d | RETURNDATASIZE | 0 0 | [0, cds] = calldata
// 3d | RETURNDATASIZE | 0 0 0 | [0, cds] = calldata
// 36 | CALLDATASIZE | cds 0 0 0 | [0, cds] = calldata
// 61 extra | PUSH2 extra | extra cds 0 0 0 | [0, cds] = calldata
mstore(
add(ptr, 0x12),
0x603836393d3d3d36610000000000000000000000000000000000000000000000
)
mstore(add(ptr, 0x1b), shl(240, extraLength))
// 01 | ADD | cds+extra 0 0 0 | [0, cds] = calldata
// 3d | RETURNDATASIZE | 0 cds 0 0 0 | [0, cds] = calldata
// 73 addr | PUSH20 0x123… | addr 0 cds 0 0 0 | [0, cds] = calldata
mstore(
add(ptr, 0x1d),
0x013d730000000000000000000000000000000000000000000000000000000000
)
mstore(add(ptr, 0x20), shl(0x60, implementation))
// 5a | GAS | gas addr 0 cds 0 0 0 | [0, cds] = calldata
// f4 | DELEGATECALL | success 0 | [0, cds] = calldata
// 3d | RETURNDATASIZE | rds success 0 | [0, cds] = calldata
// 82 | DUP3 | 0 rds success 0 | [0, cds] = calldata
// 80 | DUP1 | 0 0 rds success 0 | [0, cds] = calldata
// 3e | RETURNDATACOPY | success 0 | [0, rds] = return data (there might be some irrelevant leftovers in memory [rds, cds] when rds < cds)
// 90 | SWAP1 | 0 success | [0, rds] = return data
// 3d | RETURNDATASIZE | rds 0 success | [0, rds] = return data
// 91 | SWAP2 | success 0 rds | [0, rds] = return data
// 60 0x36 | PUSH1 0x36 | 0x36 sucess 0 rds | [0, rds] = return data
// 57 | JUMPI | 0 rds | [0, rds] = return data
// fd | REVERT | – | [0, rds] = return data
// 5b | JUMPDEST | 0 rds | [0, rds] = return data
// f3 | RETURN | – | [0, rds] = return data
mstore(
add(ptr, 0x34),
0x5af43d82803e903d91603657fd5bf30000000000000000000000000000000000
)
}
// -------------------------------------------------------------------------------------------------------------
// APPENDED DATA (Accessible from extcodecopy)
// (but also send as appended data to the delegatecall)
// -------------------------------------------------------------------------------------------------------------
extraLength -= 2;
uint256 counter = extraLength;
uint256 copyPtr = ptr + 0x43;
uint256 dataPtr;
// solhint-disable-next-line no-inline-assembly
assembly {
dataPtr := add(data, 32)
}
for (; counter >= 32; counter -= 32) {
// solhint-disable-next-line no-inline-assembly
assembly {
mstore(copyPtr, mload(dataPtr))
}
copyPtr += 32;
dataPtr += 32;
}
uint256 mask = ~(256 ** (32 - counter) - 1);
// solhint-disable-next-line no-inline-assembly
assembly {
mstore(copyPtr, and(mload(dataPtr), mask))
}
copyPtr += counter;
// solhint-disable-next-line no-inline-assembly
assembly {
mstore(copyPtr, shl(240, extraLength))
}
/// @solidity memory-safe-assembly
// solhint-disable-next-line no-inline-assembly
assembly {
// Store the `_PROXY_BYTECODE` into scratch space.
mstore(0x00, _CREATE3_PROXY_BYTECODE)
// Deploy a new contract with our pre-made bytecode via CREATE2.
let proxy := create2(0, 0x10, 0x10, salt)
// If the result of `create2` is the zero address, revert.
if iszero(proxy) {
// Store the function selector of `CreateFail()`.
mstore(0x00, 0xebfef188)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Store the proxy's address.
mstore(0x14, proxy)
// 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01).
// 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex).
mstore(0x00, 0xd694)
// Nonce of the proxy contract (1).
mstore8(0x34, 0x01)
deployed := and(
keccak256(0x1e, 0x17),
0xffffffffffffffffffffffffffffffffffffffff
)
// If the `call` fails or the code size of `deployed` is zero, revert.
// The second argument of the or() call is evaluated first, which is important
// here because extcodesize(deployed) is only non-zero after the call() to the proxy
// is made and the contract is successfully deployed.
if or(
iszero(extcodesize(deployed)),
iszero(
call(
gas(), // Gas remaining.
proxy, // Proxy's address.
value, // Ether value.
ptr, // Pointer to the creation code
creationSize, // Size of the creation code
0x00, // Offset of output.
0x00 // Length of output.
)
)
) {
// Store the function selector of `InitializeFail()`.
mstore(0x00, 0x8f86d2f1)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
}
}
}
/// @notice Returns the CREATE3 deterministic address of the contract deployed via cloneDeterministic().
/// @dev Forked from https://github.com/Vectorized/solady/blob/main/src/utils/CREATE3.sol
/// @param salt The salt used by the CREATE3 deployment
function addressOfClone3(
bytes32 salt
) internal view returns (address deployed) {
/// @solidity memory-safe-assembly
// solhint-disable-next-line no-inline-assembly
assembly {
// Cache the free memory pointer.
let m := mload(0x40)
// Store `address(this)`.
mstore(0x00, address())
// Store the prefix.
mstore8(0x0b, 0xff)
// Store the salt.
mstore(0x20, salt)
// Store the bytecode hash.
mstore(0x40, _CREATE3_PROXY_BYTECODE_HASH)
// Store the proxy's address.
mstore(0x14, keccak256(0x0b, 0x55))
// Restore the free memory pointer.
mstore(0x40, m)
// 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01).
// 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex).
mstore(0x00, 0xd694)
// Nonce of the proxy contract (1).
mstore8(0x34, 0x01)
deployed := and(
keccak256(0x1e, 0x17),
0xffffffffffffffffffffffffffffffffffffffff
)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
/***************************\
|*-*-*-* TYPES *-*-*-*|
\***************************/
enum QA_METHOD {
AI,
COMMUNITY,
VOTE
}
enum Status {
upcoming,
inProgress,
paused,
finished,
claimable,
finalized
}
enum Finalizer {
protocolAdmin,
finalizerOracle
}
struct CampaignInitData {
uint64 initTime;
address clonedCampaign;
address implementation;
}
struct BasicInfo {
string name;
string ipfsCID;
address owner;
uint256 startTime;
uint256 endTime;
}
struct PostInfo {
uint256 totalPosts;
uint256 totalEligibleContents;
uint256 totalEffectiveKPIs;
uint256 lastPostTime;
uint256 lastUpdatedTime;
uint256 applicationFee;
}
struct Token {
string name;
string symbol;
uint256 decimals;
}
struct BudgetInfo {
address token;
uint128 maxPerPost;
uint128 reservedAmount;
}
struct PostData {
string postLink;
address applicant;
}
struct QualificationData {
QA_METHOD method;
uint88 pct;
address oracle;
string kind;
}
struct KPI {
uint8 pct;
uint248 min;
uint256 ratio;
string method;
string extra;
}
struct SocialKPIs {
string social;
KPI[] kpis;
}
/**
* @title IEchoCampaign
* @notice Defines the interface for Echo Campaign contracts, which manage campaign
* settings, budgets, KPIs, content submissions, and reward distribution.
* @dev This interface provides function definitions for interacting with campaign
* data, including retrieving budget details, tracking content KPIs, and
* managing administrative actions.
* @author Dynabits.org
*/
interface IEchoCampaign {
/*******************************\
|*-*-*-* EXTERNALS *-*-*-*-*|
\*******************************/
/**
* @notice Initializes the contract with provided parameters.
* @dev This function can only be called by the factory contract.
* It sets the IPFS CID, budget information, social KPIs, and qualification data.
* Additionally, it registers action kinds based on QA methods and social KPIs.
* @param ipfsCID The IPFS Content Identifier (CID) associated with the data.
* @param budgetInfo_ Struct containing budget-related information.
* @param socialKPIs_ Struct containing social Key Performance Indicators (KPIs).
* @param qaData_ An array of QualificationData structs defining QA methods and kinds.
* Reverts:
* - `ONLY_FACTORY` if the caller is not the factory contract.
* - `DUPLICATE_QA` if there is a duplicate qualification.
* - `DUPLICATE_KPI` if there is a duplicate KPI.
*/
function init(
string calldata ipfsCID,
BudgetInfo calldata budgetInfo_,
SocialKPIs calldata socialKPIs_,
QualificationData[] calldata qaData_
) external;
/**
* @notice Toggles the campaign's pause state.
* @dev Only callable by the protocol administrator.
*
* Emits:
* - `CampaignPauseToggled` with the new state.
*
* Reverts:
* - `UNAUTHORIZED_CALLER` if the caller is not the protocol administrator.
*/
function togglePauseCampaign() external;
/**
* @notice Finalizes a campaign that is in progress.
* @dev Only callable by the protocol administrator when the campaign status is `inProgress`.
* Refunds any unspent budget and marks the campaign as finalized.
*
* Emits:
* - `CampaignFinalized` with the protocol administrator as the finalizer and the refunded amount.
*
* Reverts:
* - `UNAUTHORIZED_CALLER` if the caller is not the protocol administrator.
* - `ONLY_IN_PROGRESS_STATUS` if the campaign is not currently in progress.
*/
function finalizeInProgressCampaign() external;
/**
* @notice Increases the campaign budget by a specified amount.
* @dev Transfers the specified amount of tokens from the caller to the campaign
* and updates the reserved budget.
* @param amount The amount of tokens to add to the campaign budget.
*
* Emits:
* - `BudgetIncreased` with the added amount, previous budget, new budget, and sender address.
*
* Reverts:
* - `ONLY_UPCOMING_OR_IN_PROGRESS_STATUS` if the campaign is not in `upcoming` or `inProgress` status.
*/
function increaseBudget(uint256 amount) external;
/**
* @notice Submits content for the campaign.
* @dev Adds a content link to the campaign without performing authentication checks at submission time.
* Authentication is enforced at the claiming stage.
* @param contentLink The link to the submitted content.
*
* Reverts:
* - `ONLY_IN_PROGRESS_STATUS` if the campaign is not in `inProgress` status.
* - `INVALID_CONTENT_LINK` if the provided content link is empty.
*/
function applyContent(string calldata contentLink) external;
/**
* @notice Finalizes a campaign that has reached the finished status.
* @dev Ensures the campaign is in the `finished` status before finalization.
* Refunds any unspent budget and marks the campaign as finalized.
*
* Emits:
* - `CampaignFinalized` with the finalizer and the refunded amount.
*
* Reverts:
* - `ONLY_FINISHED_CAMPAIGN_STATUS` if the campaign is not in `finished` status.
*/
function finalizeFinishedCampaign() external;
/**
* @notice Updates the KPI metrics for a specific content link.
* @dev Modifies the total and per-content effective KPIs based on the provided values.
* If the content has no prior KPI value, it is counted as an eligible content.
*
* @param contentLink The unique identifier (link) of the content being updated.
* @param additionalContentEffectiveKPI The KPI value to be added or subtracted.
* @param additional A boolean indicating whether to increase or decrease the KPI.
*
* @return totalEffectiveKPIs The updated total sum of all effective KPIs.
* @return contentEffectiveKPI_ The updated KPI for the specified content.
* @return totalEligibleContents The updated count of eligible contents.
* @return eligibilityPolarity must be either 1, 0, or -1 and determines how the total eligible content count is updated.
*
* Requirements:
* - Caller must be the EchoContetns contract.
*/
function updateKPIs(
string calldata contentLink,
uint256 additionalContentEffectiveKPI,
bool additional
)
external
returns (
uint256 totalEffectiveKPIs,
uint256 contentEffectiveKPI_,
uint256 totalEligibleContents,
int8 eligibilityPolarity
);
/**
* @notice Allows the reward claim for registered and eligible content whose author has been registered by the oracle.
* @dev This function verifies that the campaign status is `claimable`, ensures the content is registered,
* checks its effective KPI score, calculates the reward based on the KPI ratio, and distributes
* the appropriate amount. If a max reward per post is defined, any excess funds are refunded.
* Once all eligible claims have been processed, the campaign is finished.
* @param contentLink The unique identifier (URL) of the content for which the claim is being made.
*
* Requirements:
* - The campaign status must be `claimable`.
* - The content must be registered under the campaign.
* - The content must have an effective KPI score greater than zero.
*
* Emits:
* - `Claimed` event upon successful claim, indicating the content, recipient, reward amount, and refund amount.
* - `CampaignFinished` event is emitted when all eligible contents have been claimed and the campaign is finished.
*
* Reverts:
* - `ONLY_CLAIMABLE_STATUS()` if the campaign is not in the `claimable` status.
* - `INVALID_CONTENT_LINK()` if the content is not registered in the campaign.
* - `NO_CONTENT_AUTHOR()` if the content's author has not been registered by the oracle before claiming.
* - `NOT_ELIGIBLE_FOR_WITHDRAW()` if the content has no effective KPI score.
*/
function claim(string calldata contentLink) external;
/*****************************\
|-*-*-*-* GETTERS *-*-*-*-|
\*****************************/
/// @notice Retrieves the budget information for the campaign.
/// @return budgetInfo_ The budget details, including reserved amount and token information.
function budgetInfo() external view returns (BudgetInfo memory budgetInfo_);
/// @notice Retrieves the social KPIs associated with the campaign.
/// @return socialKPIs_ The set of social KPIs used for evaluating campaign performance.
function socialKPIs() external view returns (SocialKPIs memory socialKPIs_);
/// @notice Retrieves the qualification data for the campaign.
/// @return qaData_ An array of qualification data required for content evaluation.
function qaData()
external
view
returns (QualificationData[] memory qaData_);
/// @notice Checks whether a specific action kind is registered in the campaign.
/// @param actionKind The string representation of the action kind to check.
/// @return isRegistered A boolean indicating whether the action kind is registered.
function registeredActionKind(string calldata actionKind)
external
view
returns (bool);
/// @notice Retrieves the effective KPI score for a given content link.
/// @param contentLink The unique identifier (URL or hash) of the submitted content.
/// @return kpiScore The effective KPI score assigned to the content.
function contentEffectiveKPI(string calldata contentLink)
external
view
returns (uint256);
/**
* @notice Estimates the reward amount for content based on provided QA and KPI data.
* @dev Ensures QA and KPI inputs match the campaign's predefined requirements.
* @param qas An array of qualification data for the content.
* @param kpis An array of KPI data for the content.
* @return rewardAmount The estimated reward amount in campaign token units.
*/
function rewardEstimation(
QualificationData[] calldata qas,
KPI[] calldata kpis
) external view returns (uint256 rewardAmount);
/**
* @notice Retrieves comprehensive details about the campaign.
* @return basicInfo_ Basic campaign details such as name, owner, and time period.
* @return initData_ Initialization data from the campaign factory.
* @return budgetInfo_ Budget details including token and reserved amounts.
* @return token_ Token metadata such as name, symbol, and decimals.
* @return socialKPIs_ The social KPI details of the campaign.
* @return qaData_ The qualification data required for content.
* @return postInfo_ Statistics related to campaign content submissions.
* @return status_ The current status of the campaign.
*/
function details()
external
view
returns (
BasicInfo memory basicInfo_,
CampaignInitData memory initData_,
BudgetInfo memory budgetInfo_,
Token memory token_,
SocialKPIs memory socialKPIs_,
QualificationData[] memory qaData_,
PostInfo memory postInfo_,
Status status_
);
/**
* @notice Returns the address of the person who created the specified content and is eligible to claim it.
* @param contentLink The URL of the submitted content.
* @return The wallet address of the content creator for the given content.
May return the zero address (0x0) if no record is found in the oracle.
*/
function getContentCreator(string calldata contentLink)
external
view
returns (address);
/// @notice Retrieves the current status of the campaign.
/// @return status_ The campaign's status as an enum value.
function currentStatus() external view returns (Status status_);
/// @notice Calculates the application fee required for content submission.
/// @return fee The total application fee in campaign token units.
function applicationFee() external view returns (uint256 fee);
/// @notice Retrieves the campaign's name.
/// @return campaignName The human-readable name of the campaign.
function name() external view returns (string memory campaignName);
/// @notice Retrieves the address of the Echo Contents contract.
/// @return contentsAddress The address of the content management contract.
function contents() external view returns (address contentsAddress);
/// @notice Retrieves the address of the Echo Administration contract.
/// @return adminAddress The address of the admin contract.
function admin() external pure returns (address adminAddress);
/// @notice Retrieves the address of the campaign factory.
/// @return factoryAddress The address of the campaign factory contract.
function factory() external pure returns (address factoryAddress);
/// @notice Retrieves the owner of the campaign.
/// @return ownerAddress The address of the campaign owner.
function owner() external pure returns (address ownerAddress);
/// @notice Retrieves the refund address for the campaign.
/// @return refundAddr The address where refunds are sent.
function refundAddress() external pure returns (address refundAddr);
/// @notice Retrieves the hashed name of the campaign.
/// @return campaignNameHash The bytes32 representation of the campaign name.
function nameHash() external pure returns (bytes32 campaignNameHash);
/// @notice Retrieves the start time of the campaign.
/// @return startTimestamp The Unix timestamp of the campaign start time.
function startTime() external pure returns (uint256 startTimestamp);
/// @notice Retrieves the end time of the campaign.
/// @return endTimestamp The Unix timestamp of the campaign end time.
function endTime() external pure returns (uint256 endTimestamp);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
import {CampaignInitData, BudgetInfo, SocialKPIs, QualificationData} from "./IEchoCampaign.sol";
/***************************\
|*-*-*-* TYPES *-*-*-*|
\***************************/
/// @notice Structure containing the initialization parameters for a new campaign.
/// @dev This struct encapsulates all key configurations required when creating a campaign.
struct InitCampaign {
uint256 startTime; /// Start time of the campaign (Unix timestamp).
uint256 endTime; /// End time of the campaign (Unix timestamp).
address refundAddress; /// Address to receive refunds (required if maxPerPost is set).
string ipfsCID; /// IPFS content identifier (CID) for storing metadata.
BudgetInfo budgetInfo; /// Budget-related configurations for the campaign.
SocialKPIs socialKPIs; /// Social Key Performance Indicators (KPIs) for the campaign.
QualificationData[] qaData; /// Array of qualification methods used for campaign validation.
}
/// @title Echo Campaign Factory Interface
/// @notice This interface defines the core functionality for deploying campaign contracts.
/// @author Dynabits.org
interface IEchoCampaignFactory {
/*******************************\
|*-*-*-* EXTERNALS *-*-*-*-*|
\*******************************/
/// @notice Updates the campaign implementation contract.
/// @dev This function allows the protocol admin to change the underlying campaign logic by deploying a new contract.
/// @param newImplementation The address of the new campaign implementation.
function changeImplementation(address newImplementation) external;
/**
* @notice Deploys a new campaign contract with the specified parameters.
* @dev Performs necessary validation checks before deployment, including:
* - Ensuring valid input parameters (e.g., campaign name, budget, duration).
* - Enforcing compliance with qualification and KPI requirements.
* - Deducting protocol fees and transferring budget to the campaign contract.
* @param owner The address of the campaign owner.
* @param name The unique name of the campaign (must be between 8 and 64 characters).
* @param initCampaign A struct containing all initial parameters for campaign deployment.
* @return clonedCampaign The address of the newly deployed campaign contract.
*
* Requirements:
* - `owner` must be a valid, non-zero address.
* - `name` must be unique and between 8 to 64 characters in length.
* - `initCampaign.refundAddress` must be valid if `maxPerPost` is set.
* - `initCampaign.ipfsCID` must be a non-empty string.
* - The campaign start time must not be in the past.
* - The campaign end time must be later than the start time.
* - The campaign duration must not exceed the protocol's maximum allowed time.
* - The campaign's budget token must be whitelisted.
* - `maxPerPost` must not exceed `reservedAmount`, if set.
* - At least one qualification method (QA method) must be provided.
* - The total qualification percentages (`pct`) must sum up to 100%.
* - At least one KPI must be registered.
* - The sum of KPI percentages must be exactly 100%.
* - The provided social network and KPIs must be allowed by the protocol.
*
* Effects:
* - Deploys a new campaign contract as a clone of the base implementation.
* - Deducts the protocol fee from the reserved campaign budget.
* - Initializes the campaign contract with the provided parameters.
* - Transfers the remaining budget to the newly created campaign.
* - Stores campaign metadata for tracking and uniqueness validation.
*
* Emits:
* - `CampaignCreated` event upon successful deployment of the campaign.
*
* Reverts:
* - `ZERO_ADDRESS` if `owner` or `refundAddress` (when required) is zero.
* - `NAME_LENGTH_LOWER_THAN_8_BYTES` if the name is too short.
* - `NAME_LENGTH_LONGER_THAN_64_BYTES` if the name is too long.
* - `NULL_CID` if the campaign IPFS CID is empty.
* - `NAME_EXISTS` if a campaign with the same name already exists.
* - `START_TIME_IN_PAST` if the campaign start time is invalid.
* - `END_TIME_LOWER_THAN_START_TIME` if the end time is before or equal to the start time.
* - `CHECK_MAX_CAMPAIGN_DURATION` if the duration exceeds the protocol’s limit.
* - `MAX_PER_POST_BIGGER_THAN_RESERVED_AMOUNT` if `maxPerPost` exceeds the available budget.
* - `NO_QUALIFICATION` if no qualification method is provided.
* - `QA_METHOD_OR_ORACLE_NOT_ALLOWED` if the qualification method or oracle is unauthorized.
* - `SUM_OF_QUALIFICATION_PARAMS_MUST_BE_100` if qualification percentages do not total 100%.
* - `NO_KPIS` if no KPIs are provided.
* - `SUM_OF_SOCIAL_KPI_PARAMS_MUST_BE_100` if KPI percentages do not sum to 100%.
* - `ONLY_ALLOWED_SOCIAL_KPIS` if an unapproved social KPI is used.
* - `SOCIAL_KPI_RATIO_IS_ZERO` if a KPI lacks a predefined ratio in the protocol.
*/
function createCampaign(
address owner,
string calldata name,
InitCampaign memory initCampaign
) external returns (address clonedCampaign);
/*****************************\
|-*-*-*-* GETTERS *-*-*-*-|
\*****************************/
/// @notice Retrieves the current implementation contract for campaigns.
/// @dev This address is used for cloning new campaign instances.
/// @return The address of the campaign implementation contract.
function implementation() external view returns (address);
/// @notice Retrieves the address of a deployed campaign by its index.
/// @param index The index of the campaign in the `allCampaigns` array.
/// @return The address of the deployed campaign.
function allCampaigns(uint256 index) external view returns (address);
/**
* @notice Retrieves the original campaign name from its hashed value.
* @dev Useful for verifying unique campaign names.
* @param nameHash The hashed value of the campaign name.
* @return The original campaign name.
*/
function campaignNameHashToName(bytes32 nameHash)
external
view
returns (string memory);
/// @notice Retrieves the address of the Echo Administration contract.
/// @return The address of the Echo Administration contract.
function ECHO_ADMIN() external view returns (address);
/// @notice Retrieves the address of the Echo Contents contract.
/// @return The address of the Echo Contents contract.
function ECHO_CONTENTS() external view returns (address);
/**
* @notice Retrieves initialization details for a specific campaign.
* @dev Provides useful data for off-chain applications and analytics.
* @param name The name of the deployed campaign.
* @return The timestamp of deployment, the deployed contract address, and the structural contract used.
*/
function campaignNameToInitData(string memory name)
external
view
returns (CampaignInitData memory);
/**
* @notice Retrieves a paginated list of recently deployed campaigns.
* @dev Returns 10 campaign addresses per page, with pagination for historical retrieval.
* - If `page = 0`, returns the 10 most recent campaigns.
* - Optimized for off-chain use cases as an alternative to event logs.
* @param page The page number requested.
* @return currentPage The current page number.
* @return totalPages The total number of available pages.
* @return pagedArray A list of up to 10 recently deployed campaign addresses.
*/
function paginatedCampaigns(uint256 page)
external
view
returns (
uint256 currentPage,
uint256 totalPages,
address[] memory pagedArray
);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
/***************************\
|*-*-*-* TYPES *-*-*-*|
\***************************/
enum ActionStatus {
NA,
Config,
QA,
KPI
}
struct ActionAndKind {
ActionStatus actionStatus;
string action;
string kind;
}
struct ModelData {
ActionStatus actionStatus;
uint8 socialKPIratio;
uint248 fee;
address oracle;
}
/// @title Echo Administration Interface
/// @notice Defines administrative functions for managing campaign settings, token whitelisting, and oracle permissions.
/// @author Dynabits.org
interface IEchoAdministration {
/*******************************\
|*-*-*-* EXTERNALS *-*-*-*-*|
\*******************************/
/// @notice Transfers protocol admin rights to a new address.
/// @dev Only the current protocol admin can call this function. The new admin address cannot be the zero address.
/// @param newProtocolAdmin The address of the new protocol admin.
function changeProtocolAdmin(address newProtocolAdmin) external;
/// @notice Sets the campaign fee rate as a percentage of the allocated token at creation.
/// @dev The value must be between 0 and 1e5.
/// @param newCampaignFeeRate The new campaign creation fee rate.
function changeCampaignFeeRate(uint24 newCampaignFeeRate) external;
/// @notice Sets the maximum campaign duration.
/// @dev A value of zero means there is no maximum campaign duration.
/// @param newMaxCampaignTime The maximum campaign duration in seconds.
function setMaxCampaignTime(uint72 newMaxCampaignTime) external;
/// @notice Whitelists a new ERC20 token for use in campaigns.
/// @dev A token cannot be whitelisted more than once. The whitelist status is checked during campaign creation.
/// @param token The ERC20 token address.
function whitelistToken(address token) external;
/// @notice Removes an ERC20 token from the whitelist, preventing its selection as a campaign budget token.
/// @param token The ERC20 token address.
function removeWhitelistedToken(address token) external;
/// @notice Toggles full access permissions for an oracle.
/// @dev This function can be used to temporarily or permanently disable an oracle’s access to campaign-related functionalities.
/// @param oracle The address of the oracle.
function toggleOracleFullAccess(address oracle) external;
/**
* @notice Sets or updates the model data for a given action and kind.
* @dev Configures an action-kind mapping with an associated oracle, fee, and status.
* @param actionAndKind The struct containing the action and kind identifiers.
* @param model The struct containing model details such as the oracle, fee, status, and social KPI ratio.
*
* Requirements:
* - `actionAndKind.action` and `actionAndKind.kind` must not be empty.
* - `model.actionStatus` must be one of `QA`, `KPI`, or `CONFIG`.
* - Actions must match their respective `actionStatus`:
* - `QA` actions: `QA-AI`, `QA-C`, `QA-V`
* - `KPI` actions: `KPI`
* - `CONFIG` actions: `CONFIG`
* - QA oracles must have a nonzero fee.
* - KPI oracles must have both a nonzero fee and a nonzero social KPI ratio.
* - CONFIG actions always have a zero fee.
*
* Effects:
* - Adds or updates an action-kind mapping.
* - Removes model data if `model.oracle` is zero.
*
* Emits:
* - `ModelSettled` if a model is set or updated.
* - `ModelRemoved` if a model is removed.
*
* Reverts:
* - `EMPTY_ACTION_OR_KIND_PROVIDED` if either action or kind is empty.
* - `NA_ACTION_STATUS` if the provided action status is `NA`.
* - `NOT_RELATIVE_ACTION` if the action does not match its required type.
* - `UNDEFINED_PROCESS` if a QA oracle has a fee but no valid address.
* - `ZERO_FEE_PROVIDED` if a required fee is zero.
* - `ZERO_SOCIAL_KPI_PROVIDED` if a required social KPI ratio is zero.
*/
function setModel(
ActionAndKind calldata actionAndKind,
ModelData calldata model
) external;
/*****************************\
|-*-*-*-* GETTERS *-*-*-*-|
\*****************************/
/// @notice Returns the campaign creation fee rate as a percentage.
/// @dev This value is deducted from the allocated budget immediately upon campaign creation.
/// @return The campaign creation fee rate.
function campaignFeeRate() external view returns (uint24);
/// @notice Returns the maximum allowed duration for campaigns.
/// @dev A campaign’s duration (end time - start time) must be less than or equal to this value at creation.
/// @return The maximum campaign duration in seconds.
function maxCampaignTime() external view returns (uint72);
/// @notice Returns the protocol admin address.
/// @dev The protocol admin has full access to infrastructural modifications.
/// @return The address of the protocol admin.
function protocolAdmin() external view returns (address);
/// @notice Checks if a given ERC20 token is whitelisted for campaign use.
/// @param token The ERC20 token address.
/// @return A boolean indicating whether the token is whitelisted.
function whitelistedToken(address token) external view returns (bool);
/**
* @notice Checks if an oracle has been banned.
* @dev A banned oracle loses all permissions to interact with EchoContents and campaign contracts.
* @param oracle The address of the oracle.
* @return A boolean indicating whether the oracle is banned.
*/
function disallowedOracle(address oracle) external view returns (bool);
/**
* @notice Returns the number of available methods and parameters for a given action.
* @dev Examples:
* - KPI actions (e.g., `X`) can have parameters like `Like` and `Retweet`.
* - Qualification actions (`QA-V`) can have parameters like `Accuracy` and `Originality`.
* @param action The action type, such as `X`, `QA-V`, or `Config`.
* @return The number of available methods for the action.
*/
function totalKindMethods(string calldata action)
external
view
returns (uint256);
/**
* @notice Retrieves the original action and kind from a given hashed identifier.
* @dev Useful for reducing gas costs and simplifying comparisons.
* @param hashedActionKind The hash of the concatenated action and kind.
* @return The decoded action and kind as a string.
*/
function hashToActionKind(bytes32 hashedActionKind)
external
view
returns (string memory);
/**
* @notice Returns model data associated with an oracle and a hashed action-kind.
* @param oracle_ The address of the oracle.
* @param hashedActionKind The hashed action-kind identifier.
* @return actionStatus The action status of the model.
* @return socialKPIratio The social KPI ratio.
* @return fee The associated fee for the action.
* @return oracle The address of the oracle managing the action.
*/
function modelData(address oracle_, bytes32 hashedActionKind)
external
view
returns (
ActionStatus actionStatus,
uint8 socialKPIratio,
uint248 fee,
address oracle
);
/**
* @notice Checks if an oracle has the required permissions for given action-kind hashes.
* @dev Access is determined based on the oracle's address:
* - `address(0)`: Checks if the hashed action-kind exists for KPI.
* - `address(1)`: Checks if the hashed action-kind exists for CONFIG.
* - Any other address: Checks if the oracle has QA access.
* @param oracle The oracle’s address.
* @param hashedActionsKind An array of hashed action-kind values to verify.
* @return A boolean indicating whether the oracle is authorized.
*/
function allowedActionKind(
address oracle,
bytes32[] calldata hashedActionsKind
) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT pragma solidity >=0.8.19; /* ██████╗ ██████╗ ██████╗ ███╗ ███╗ █████╗ ████████╗██╗ ██╗ ██╔══██╗██╔══██╗██╔══██╗████╗ ████║██╔══██╗╚══██╔══╝██║ ██║ ██████╔╝██████╔╝██████╔╝██╔████╔██║███████║ ██║ ███████║ ██╔═══╝ ██╔══██╗██╔══██╗██║╚██╔╝██║██╔══██║ ██║ ██╔══██║ ██║ ██║ ██║██████╔╝██║ ╚═╝ ██║██║ ██║ ██║ ██║ ██║ ╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ██╗ ██╗██████╗ ██████╗ ██████╗ ██╗ ██╗ ██╗ █████╗ ██║ ██║██╔══██╗██╔════╝ ██╔═████╗╚██╗██╔╝███║██╔══██╗ ██║ ██║██║ ██║███████╗ ██║██╔██║ ╚███╔╝ ╚██║╚█████╔╝ ██║ ██║██║ ██║██╔═══██╗████╔╝██║ ██╔██╗ ██║██╔══██╗ ╚██████╔╝██████╔╝╚██████╔╝╚██████╔╝██╔╝ ██╗ ██║╚█████╔╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚════╝ */ import "./ud60x18/Casting.sol"; import "./ud60x18/Constants.sol"; import "./ud60x18/Conversions.sol"; import "./ud60x18/Errors.sol"; import "./ud60x18/Helpers.sol"; import "./ud60x18/Math.sol"; import "./ud60x18/ValueType.sol";
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
import {IEchoAdministration} from "./IEchoAdministration.sol";
import {IEchoCampaignFactory} from "./IEchoCampaignFactory.sol";
import {QA_METHOD} from "./IEchoCampaign.sol";
import {UD60x18} from "@prb/math/src/UD60x18.sol";
/***************************\
|*-*-*-* TYPES *-*-*-*|
\***************************/
struct ConfigOracleData {
string kind;
bytes value;
}
struct QualificationOracleData {
QA_METHOD method;
uint256 pct;
string kind;
}
struct KPIsOracleData {
string method;
uint256 value;
}
struct SocialKPIsOracleData {
string social;
KPIsOracleData[] kpis;
}
struct CampaignAndContent {
address campaign;
string content;
}
struct CampaignContentInfo {
uint128 lastUpdatedTime;
uint128 lastPostTime;
string[] contents;
}
struct ContentDetails {
bool contentRegistered;
uint8 totalQaKinds;
uint240 lastUpdatedTime;
UD60x18 contentOverallQaScore;
mapping(string => uint256) actionKindToValue;
}
struct ContentsData {
uint256 totalEffectiveKPIs;
uint256 totalPosts;
uint256 totalEligibleContents;
}
/**
* @title Echo Contents Interface
* @notice Manages content within Echo campaigns by enabling registration, updates, and evaluation.
* @dev This interface facilitates content tracking, quality assessment through oracles, and KPI updates.
* - Allows campaigns to associate and manage content.
* - Supports qualification and configuration updates via oracles and protocol admin.
* - Enables KPI tracking based on predefined metrics.
* - Provides getter functions for efficient content retrieval and analytics.
* @author Dynabits.org
*/
interface IEchoContents {
/*******************************\
|*-*-*-* EXTERNALS *-*-*-*-*|
\*******************************/
/**
* @notice Registers a new content link for a campaign.
* @dev Associates content with a campaign, ensuring only authorized campaigns can register.
* @param contentLink The unique identifier (e.g., IPFS hash) of the content.
* @param campaignMaxPerPost The campaign’s maximum payment per post.
*
* Requirements:
* - The caller must be a registered campaign.
* - The content must not have been previously registered for the same campaign.
*
* Effects:
* - Stores the content reference associated with the campaign.
* - Updates the campaign’s content records, including the last post timestamp.
*
* Emits:
* - `ContentApplied` upon successful registration.
*
* Reverts:
* - `ACCESS_DENIED` if the caller is not a registered campaign.
* - `CONTENT_REGISTERED_FOR_CAMPAIGN_BEFORE` if already registered.
*/
function addContentLink(string calldata contentLink, uint256 campaignMaxPerPost) external;
/**
* @notice Updates configuration and qualification data for a specific content in a campaign.
* @dev Allows oracles or the protocol admin to set content-related data.
* @param campaign The campaign's address.
* @param contentLink The unique identifier of the content.
* @param configs An array of configuration data from the oracle.
* @param qualifications Qualification data used to evaluate the content.
*
* Requirements:
* - At least one configuration or qualification must be provided.
* - The caller must be the protocol admin or an authorized oracle.
* - If the caller is an oracle, they must not be banned.
* - The content must be registered under the campaign.
* - Configuration updates require appropriate oracle permissions.
* - Qualification updates must adhere to campaign-specific rules and valid percentage ranges.
*
* Emits:
* - `ContentConfigsAdded` on successful configuration update.
* - `NewQualificationsSettled` on successful qualification update.
* - `ContentQAoverallScore` if the final QA score is calculated.
* - `ReadyForKPIupdates` if the content qualifies for KPI updates.
*
* Reverts:
* - `UNDEFINED_JOB` if no configurations or qualifications are provided.
* - `ORACLE_IS_BAN` if the caller is a banned oracle.
* - `CONTENT_DOESNT_EXIST` if the content is unregistered.
* - `ORACLE_DOESNT_HAVE_REQUIRED_ACCESS` if the caller lacks permissions.
* - `GIVEN_QA_KIND_DOESNT_EXIST_ON_CAMPAIGN` if an invalid qualification is provided.
* - `QA_DATA_SETTLED_BEFORE` if qualifications were previously set and the caller is not the admin.
* - `CHECK_QA_PCT` if the qualification percentage is out of range.
*/
function setContentData(
address campaign,
string calldata contentLink,
ConfigOracleData[] calldata configs,
QualificationOracleData[] calldata qualifications
) external;
/**
* @notice Updates the KPI data for registered content within a campaign.
* @dev Only the protocol admin or an authorized KPI oracle can update KPIs.
* If all required KPIs are met, the Effective KPI formula is applied.
* @param campaign The address of the campaign.
* @param contentLink The unique identifier of the content (e.g., IPFS hash).
* @param socialKPIs The KPI data associated with the campaign’s social platform.
*
* Requirements:
* - The caller must be the protocol admin or an authorized KPI oracle.
* - A banned oracle cannot update KPIs unless they are the admin.
* - The content must be registered under the specified campaign.
* - The campaign must be in an `InProgress`, `Paused`, or `Finished` state.
* - The content must have a valid non-zero QA score.
* - The KPI action must be registered under the campaign.
* - The oracle must have the required access to modify the KPI data.
*
* Effects:
* - Updates KPI values for the specified content and campaign.
* - Computes the Effective KPI value based on campaign rules and content QA score.
* - Updates the campaign’s total Effective KPIs and eligible content count.
*
* Emits:
* - `KPIsUpdated` when a KPI update is successfully processed.
* - `ContentEffectiveKPIupdated` when the Effective KPI score is updated.
*
* Reverts:
* - `ORACLE_IS_BAN` if the caller is a banned oracle.
* - `CONTENT_DOESNT_EXIST` if the content is not registered.
* - `ONLY_IN_PROGRESS_OR_PAUSED_OR_FINISHED_CAMPAIGN` if the campaign is in an invalid state.
* - `ZERO_CONTENT_QA_SCORE` if the content has a QA score of zero.
* - `DIFFERENT_SOCIALS` if the provided KPI does not match the campaign’s social KPI.
* - `GIVEN_KPI_KIND_DOESNT_EXIST_ON_CAMPAIGN` if the KPI type is not registered in the campaign.
* - `ORACLE_DOESNT_HAVE_REQUIRED_ACCESS` if the caller lacks KPI update permissions.
*/
function updateKPIs(
address campaign,
string calldata contentLink,
SocialKPIsOracleData calldata socialKPIs
) external;
/*****************************\
|-*-*-*-* GETTERS *-*-*-*-|
\*****************************/
/**
* @notice Retrieve the registered content along with its associated campaign address.
* @param index The index in the contents array.
* @return campaign The address of the campaign associated with the content.
* @return content The registered content.
*/
function contents(uint256 index)
external
view
returns (address campaign, string memory content);
/// @notice Retrieves the address of the Echo Administration contract.
/// @return The address of the Echo Administration contract.
function ECHO_ADMIN() external view returns (IEchoAdministration);
/// @notice Retrieves the address of the Echo Campaign Factory contract.
/// @return The address of the Echo Campaign Factory contract.
function ECHO_FACTORY() external view returns (IEchoCampaignFactory);
/**
* @notice Retrieve the content-related timestamps for a given campaign.
* @param campaign The address of the campaign.
* @return lastUpdatedTime The timestamp of the last update made to the campaign's content.
* @return lastPostTime The timestamp of the last content post associated with the campaign.
*/
function campaignContentInfo(address campaign)
external
view
returns (uint256 lastUpdatedTime, uint256 lastPostTime);
/**
* @notice Retrieve information about a specific content registered for a given campaign.
* @param campaign The address of the campaign associated with the content.
* @param contentLink The unique link or identifier of the content.
* @param actionKind The concatenated social platform and the intended method, such as "X: Like" (optional).
* @return contentRegistered A boolean indicating whether the content is registered under the campaign.
* @return totalQaKinds The total number of qualifications set for the content in the campaign.
* @return lastUpdatedTime The timestamp of the last update made to the content.
* @return actionKindValue The corresponding value for the provided action kind, if applicable.
*/
function contentToCampaign(
address campaign,
string calldata contentLink,
string calldata actionKind
)
external
view
returns (
bool contentRegistered,
uint256 totalQaKinds,
uint256 lastUpdatedTime,
uint256 actionKindValue
);
/**
* @notice Returns recent registered contents and their campaigns in paginated format.
* @dev If page = 0, returns the last 10 registered contents.
* @param page The page number.
* @return currentPage The current page number.
* @return totalPages The total number of pages.
* @return pagedArray A list of 10 recent contents with campaign addresses.
*/
function paginatedContentsWithCampaigns(uint256 page)
external
view
returns (
uint256 currentPage,
uint256 totalPages,
CampaignAndContent[] memory pagedArray
);
/**
* @notice Returns recent registered contents for a specific campaign in paginated format.
* @dev If page = 0, returns the last 10 contents for the campaign.
* @param campaign The campaign's address.
* @param page The page number.
* @return currentPage The current page number.
* @return totalPages The total number of pages.
* @return pagedArray A list of 10 recent contents associated with the campaign.
*/
function paginatedCampaignContents(uint256 page, address campaign)
external
view
returns (
uint256 currentPage,
uint256 totalPages,
string[] memory pagedArray
);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "./Casting.sol" as Casting;
import "./Helpers.sol" as Helpers;
import "./Math.sol" as Math;
/// @notice The unsigned 60.18-decimal fixed-point number representation, which can have up to 60 digits and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the Solidity type uint256.
/// @dev The value type is defined here so it can be imported in all other files.
type UD60x18 is uint256;
/*//////////////////////////////////////////////////////////////////////////
CASTING
//////////////////////////////////////////////////////////////////////////*/
using {
Casting.intoSD1x18,
Casting.intoUD2x18,
Casting.intoSD59x18,
Casting.intoUint128,
Casting.intoUint256,
Casting.intoUint40,
Casting.unwrap
} for UD60x18 global;
/*//////////////////////////////////////////////////////////////////////////
MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
// The global "using for" directive makes the functions in this library callable on the UD60x18 type.
using {
Math.avg,
Math.ceil,
Math.div,
Math.exp,
Math.exp2,
Math.floor,
Math.frac,
Math.gm,
Math.inv,
Math.ln,
Math.log10,
Math.log2,
Math.mul,
Math.pow,
Math.powu,
Math.sqrt
} for UD60x18 global;
/*//////////////////////////////////////////////////////////////////////////
HELPER FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
// The global "using for" directive makes the functions in this library callable on the UD60x18 type.
using {
Helpers.add,
Helpers.and,
Helpers.eq,
Helpers.gt,
Helpers.gte,
Helpers.isZero,
Helpers.lshift,
Helpers.lt,
Helpers.lte,
Helpers.mod,
Helpers.neq,
Helpers.not,
Helpers.or,
Helpers.rshift,
Helpers.sub,
Helpers.uncheckedAdd,
Helpers.uncheckedSub,
Helpers.xor
} for UD60x18 global;
/*//////////////////////////////////////////////////////////////////////////
OPERATORS
//////////////////////////////////////////////////////////////////////////*/
// The global "using for" directive makes it possible to use these operators on the UD60x18 type.
using {
Helpers.add as +,
Helpers.and2 as &,
Math.div as /,
Helpers.eq as ==,
Helpers.gt as >,
Helpers.gte as >=,
Helpers.lt as <,
Helpers.lte as <=,
Helpers.or as |,
Helpers.mod as %,
Math.mul as *,
Helpers.neq as !=,
Helpers.not as ~,
Helpers.sub as -,
Helpers.xor as ^
} for UD60x18 global;// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import { wrap } from "./Casting.sol";
import {
uEXP_MAX_INPUT,
uEXP2_MAX_INPUT,
uHALF_UNIT,
uLOG2_10,
uLOG2_E,
uMAX_UD60x18,
uMAX_WHOLE_UD60x18,
UNIT,
uUNIT,
uUNIT_SQUARED,
ZERO
} from "./Constants.sol";
import { UD60x18 } from "./ValueType.sol";
/*//////////////////////////////////////////////////////////////////////////
MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Calculates the arithmetic average of x and y using the following formula:
///
/// $$
/// avg(x, y) = (x & y) + ((xUint ^ yUint) / 2)
/// $$
///
/// In English, this is what this formula does:
///
/// 1. AND x and y.
/// 2. Calculate half of XOR x and y.
/// 3. Add the two results together.
///
/// This technique is known as SWAR, which stands for "SIMD within a register". You can read more about it here:
/// https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// @param x The first operand as a UD60x18 number.
/// @param y The second operand as a UD60x18 number.
/// @return result The arithmetic average as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function avg(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
uint256 yUint = y.unwrap();
unchecked {
result = wrap((xUint & yUint) + ((xUint ^ yUint) >> 1));
}
}
/// @notice Yields the smallest whole number greater than or equal to x.
///
/// @dev This is optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional
/// counterparts. See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x must be less than or equal to `MAX_WHOLE_UD60x18`.
///
/// @param x The UD60x18 number to ceil.
/// @param result The smallest whole number greater than or equal to x, as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function ceil(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
if (xUint > uMAX_WHOLE_UD60x18) {
revert Errors.PRBMath_UD60x18_Ceil_Overflow(x);
}
assembly ("memory-safe") {
// Equivalent to `x % UNIT`.
let remainder := mod(x, uUNIT)
// Equivalent to `UNIT - remainder`.
let delta := sub(uUNIT, remainder)
// Equivalent to `x + remainder > 0 ? delta : 0`.
result := add(x, mul(delta, gt(remainder, 0)))
}
}
/// @notice Divides two UD60x18 numbers, returning a new UD60x18 number.
///
/// @dev Uses {Common.mulDiv} to enable overflow-safe multiplication and division.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
///
/// @param x The numerator as a UD60x18 number.
/// @param y The denominator as a UD60x18 number.
/// @param result The quotient as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function div(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(Common.mulDiv(x.unwrap(), uUNIT, y.unwrap()));
}
/// @notice Calculates the natural exponent of x using the following formula:
///
/// $$
/// e^x = 2^{x * log_2{e}}
/// $$
///
/// @dev Requirements:
/// - x must be less than 133_084258667509499441.
///
/// @param x The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
// This check prevents values greater than 192e18 from being passed to {exp2}.
if (xUint > uEXP_MAX_INPUT) {
revert Errors.PRBMath_UD60x18_Exp_InputTooBig(x);
}
unchecked {
// Inline the fixed-point multiplication to save gas.
uint256 doubleUnitProduct = xUint * uLOG2_E;
result = exp2(wrap(doubleUnitProduct / uUNIT));
}
}
/// @notice Calculates the binary exponent of x using the binary fraction method.
///
/// @dev See https://ethereum.stackexchange.com/q/79903/24693
///
/// Requirements:
/// - x must be less than 192e18.
/// - The result must fit in UD60x18.
///
/// @param x The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp2(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
// Numbers greater than or equal to 192e18 don't fit in the 192.64-bit format.
if (xUint > uEXP2_MAX_INPUT) {
revert Errors.PRBMath_UD60x18_Exp2_InputTooBig(x);
}
// Convert x to the 192.64-bit fixed-point format.
uint256 x_192x64 = (xUint << 64) / uUNIT;
// Pass x to the {Common.exp2} function, which uses the 192.64-bit fixed-point number representation.
result = wrap(Common.exp2(x_192x64));
}
/// @notice Yields the greatest whole number less than or equal to x.
/// @dev Optimized for fractional value inputs, because every whole value has (1e18 - 1) fractional counterparts.
/// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
/// @param x The UD60x18 number to floor.
/// @param result The greatest whole number less than or equal to x, as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function floor(UD60x18 x) pure returns (UD60x18 result) {
assembly ("memory-safe") {
// Equivalent to `x % UNIT`.
let remainder := mod(x, uUNIT)
// Equivalent to `x - remainder > 0 ? remainder : 0)`.
result := sub(x, mul(remainder, gt(remainder, 0)))
}
}
/// @notice Yields the excess beyond the floor of x using the odd function definition.
/// @dev See https://en.wikipedia.org/wiki/Fractional_part.
/// @param x The UD60x18 number to get the fractional part of.
/// @param result The fractional part of x as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function frac(UD60x18 x) pure returns (UD60x18 result) {
assembly ("memory-safe") {
result := mod(x, uUNIT)
}
}
/// @notice Calculates the geometric mean of x and y, i.e. $\sqrt{x * y}$, rounding down.
///
/// @dev Requirements:
/// - x * y must fit in UD60x18.
///
/// @param x The first operand as a UD60x18 number.
/// @param y The second operand as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function gm(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
uint256 yUint = y.unwrap();
if (xUint == 0 || yUint == 0) {
return ZERO;
}
unchecked {
// Checking for overflow this way is faster than letting Solidity do it.
uint256 xyUint = xUint * yUint;
if (xyUint / xUint != yUint) {
revert Errors.PRBMath_UD60x18_Gm_Overflow(x, y);
}
// We don't need to multiply the result by `UNIT` here because the x*y product picked up a factor of `UNIT`
// during multiplication. See the comments in {Common.sqrt}.
result = wrap(Common.sqrt(xyUint));
}
}
/// @notice Calculates the inverse of x.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x must not be zero.
///
/// @param x The UD60x18 number for which to calculate the inverse.
/// @return result The inverse as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function inv(UD60x18 x) pure returns (UD60x18 result) {
unchecked {
result = wrap(uUNIT_SQUARED / x.unwrap());
}
}
/// @notice Calculates the natural logarithm of x using the following formula:
///
/// $$
/// ln{x} = log_2{x} / log_2{e}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
/// - The precision isn't sufficiently fine-grained to return exactly `UNIT` when the input is `E`.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The UD60x18 number for which to calculate the natural logarithm.
/// @return result The natural logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function ln(UD60x18 x) pure returns (UD60x18 result) {
unchecked {
// Inline the fixed-point multiplication to save gas. This is overflow-safe because the maximum value that
// {log2} can return is ~196_205294292027477728.
result = wrap(log2(x).unwrap() * uUNIT / uLOG2_E);
}
}
/// @notice Calculates the common logarithm of x using the following formula:
///
/// $$
/// log_{10}{x} = log_2{x} / log_2{10}
/// $$
///
/// However, if x is an exact power of ten, a hard coded value is returned.
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The UD60x18 number for which to calculate the common logarithm.
/// @return result The common logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function log10(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
if (xUint < uUNIT) {
revert Errors.PRBMath_UD60x18_Log_InputTooSmall(x);
}
// Note that the `mul` in this assembly block is the standard multiplication operation, not {UD60x18.mul}.
// prettier-ignore
assembly ("memory-safe") {
switch x
case 1 { result := mul(uUNIT, sub(0, 18)) }
case 10 { result := mul(uUNIT, sub(1, 18)) }
case 100 { result := mul(uUNIT, sub(2, 18)) }
case 1000 { result := mul(uUNIT, sub(3, 18)) }
case 10000 { result := mul(uUNIT, sub(4, 18)) }
case 100000 { result := mul(uUNIT, sub(5, 18)) }
case 1000000 { result := mul(uUNIT, sub(6, 18)) }
case 10000000 { result := mul(uUNIT, sub(7, 18)) }
case 100000000 { result := mul(uUNIT, sub(8, 18)) }
case 1000000000 { result := mul(uUNIT, sub(9, 18)) }
case 10000000000 { result := mul(uUNIT, sub(10, 18)) }
case 100000000000 { result := mul(uUNIT, sub(11, 18)) }
case 1000000000000 { result := mul(uUNIT, sub(12, 18)) }
case 10000000000000 { result := mul(uUNIT, sub(13, 18)) }
case 100000000000000 { result := mul(uUNIT, sub(14, 18)) }
case 1000000000000000 { result := mul(uUNIT, sub(15, 18)) }
case 10000000000000000 { result := mul(uUNIT, sub(16, 18)) }
case 100000000000000000 { result := mul(uUNIT, sub(17, 18)) }
case 1000000000000000000 { result := 0 }
case 10000000000000000000 { result := uUNIT }
case 100000000000000000000 { result := mul(uUNIT, 2) }
case 1000000000000000000000 { result := mul(uUNIT, 3) }
case 10000000000000000000000 { result := mul(uUNIT, 4) }
case 100000000000000000000000 { result := mul(uUNIT, 5) }
case 1000000000000000000000000 { result := mul(uUNIT, 6) }
case 10000000000000000000000000 { result := mul(uUNIT, 7) }
case 100000000000000000000000000 { result := mul(uUNIT, 8) }
case 1000000000000000000000000000 { result := mul(uUNIT, 9) }
case 10000000000000000000000000000 { result := mul(uUNIT, 10) }
case 100000000000000000000000000000 { result := mul(uUNIT, 11) }
case 1000000000000000000000000000000 { result := mul(uUNIT, 12) }
case 10000000000000000000000000000000 { result := mul(uUNIT, 13) }
case 100000000000000000000000000000000 { result := mul(uUNIT, 14) }
case 1000000000000000000000000000000000 { result := mul(uUNIT, 15) }
case 10000000000000000000000000000000000 { result := mul(uUNIT, 16) }
case 100000000000000000000000000000000000 { result := mul(uUNIT, 17) }
case 1000000000000000000000000000000000000 { result := mul(uUNIT, 18) }
case 10000000000000000000000000000000000000 { result := mul(uUNIT, 19) }
case 100000000000000000000000000000000000000 { result := mul(uUNIT, 20) }
case 1000000000000000000000000000000000000000 { result := mul(uUNIT, 21) }
case 10000000000000000000000000000000000000000 { result := mul(uUNIT, 22) }
case 100000000000000000000000000000000000000000 { result := mul(uUNIT, 23) }
case 1000000000000000000000000000000000000000000 { result := mul(uUNIT, 24) }
case 10000000000000000000000000000000000000000000 { result := mul(uUNIT, 25) }
case 100000000000000000000000000000000000000000000 { result := mul(uUNIT, 26) }
case 1000000000000000000000000000000000000000000000 { result := mul(uUNIT, 27) }
case 10000000000000000000000000000000000000000000000 { result := mul(uUNIT, 28) }
case 100000000000000000000000000000000000000000000000 { result := mul(uUNIT, 29) }
case 1000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 30) }
case 10000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 31) }
case 100000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 32) }
case 1000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 33) }
case 10000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 34) }
case 100000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 35) }
case 1000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 36) }
case 10000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 37) }
case 100000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 38) }
case 1000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 39) }
case 10000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 40) }
case 100000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 41) }
case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 42) }
case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 43) }
case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 44) }
case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 45) }
case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 46) }
case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 47) }
case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 48) }
case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 49) }
case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 50) }
case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 51) }
case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 52) }
case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 53) }
case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 54) }
case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 55) }
case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 56) }
case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 57) }
case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 58) }
case 100000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 59) }
default { result := uMAX_UD60x18 }
}
if (result.unwrap() == uMAX_UD60x18) {
unchecked {
// Inline the fixed-point division to save gas.
result = wrap(log2(x).unwrap() * uUNIT / uLOG2_10);
}
}
}
/// @notice Calculates the binary logarithm of x using the iterative approximation algorithm:
///
/// $$
/// log_2{x} = n + log_2{y}, \text{ where } y = x*2^{-n}, \ y \in [1, 2)
/// $$
///
/// For $0 \leq x \lt 1$, the input is inverted:
///
/// $$
/// log_2{x} = -log_2{\frac{1}{x}}
/// $$
///
/// @dev See https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
///
/// Notes:
/// - Due to the lossy precision of the iterative approximation, the results are not perfectly accurate to the last decimal.
///
/// Requirements:
/// - x must be greater than zero.
///
/// @param x The UD60x18 number for which to calculate the binary logarithm.
/// @return result The binary logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function log2(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
if (xUint < uUNIT) {
revert Errors.PRBMath_UD60x18_Log_InputTooSmall(x);
}
unchecked {
// Calculate the integer part of the logarithm.
uint256 n = Common.msb(xUint / uUNIT);
// This is the integer part of the logarithm as a UD60x18 number. The operation can't overflow because n
// n is at most 255 and UNIT is 1e18.
uint256 resultUint = n * uUNIT;
// Calculate $y = x * 2^{-n}$.
uint256 y = xUint >> n;
// If y is the unit number, the fractional part is zero.
if (y == uUNIT) {
return wrap(resultUint);
}
// Calculate the fractional part via the iterative approximation.
// The `delta >>= 1` part is equivalent to `delta /= 2`, but shifting bits is more gas efficient.
uint256 DOUBLE_UNIT = 2e18;
for (uint256 delta = uHALF_UNIT; delta > 0; delta >>= 1) {
y = (y * y) / uUNIT;
// Is y^2 >= 2e18 and so in the range [2e18, 4e18)?
if (y >= DOUBLE_UNIT) {
// Add the 2^{-m} factor to the logarithm.
resultUint += delta;
// Halve y, which corresponds to z/2 in the Wikipedia article.
y >>= 1;
}
}
result = wrap(resultUint);
}
}
/// @notice Multiplies two UD60x18 numbers together, returning a new UD60x18 number.
///
/// @dev Uses {Common.mulDiv} to enable overflow-safe multiplication and division.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
///
/// @dev See the documentation in {Common.mulDiv18}.
/// @param x The multiplicand as a UD60x18 number.
/// @param y The multiplier as a UD60x18 number.
/// @return result The product as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function mul(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(Common.mulDiv18(x.unwrap(), y.unwrap()));
}
/// @notice Raises x to the power of y.
///
/// For $1 \leq x \leq \infty$, the following standard formula is used:
///
/// $$
/// x^y = 2^{log_2{x} * y}
/// $$
///
/// For $0 \leq x \lt 1$, since the unsigned {log2} is undefined, an equivalent formula is used:
///
/// $$
/// i = \frac{1}{x}
/// w = 2^{log_2{i} * y}
/// x^y = \frac{1}{w}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2} and {mul}.
/// - Returns `UNIT` for 0^0.
/// - It may not perform well with very small values of x. Consider using SD59x18 as an alternative.
///
/// Requirements:
/// - Refer to the requirements in {exp2}, {log2}, and {mul}.
///
/// @param x The base as a UD60x18 number.
/// @param y The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function pow(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
uint256 yUint = y.unwrap();
// If both x and y are zero, the result is `UNIT`. If just x is zero, the result is always zero.
if (xUint == 0) {
return yUint == 0 ? UNIT : ZERO;
}
// If x is `UNIT`, the result is always `UNIT`.
else if (xUint == uUNIT) {
return UNIT;
}
// If y is zero, the result is always `UNIT`.
if (yUint == 0) {
return UNIT;
}
// If y is `UNIT`, the result is always x.
else if (yUint == uUNIT) {
return x;
}
// If x is greater than `UNIT`, use the standard formula.
if (xUint > uUNIT) {
result = exp2(mul(log2(x), y));
}
// Conversely, if x is less than `UNIT`, use the equivalent formula.
else {
UD60x18 i = wrap(uUNIT_SQUARED / xUint);
UD60x18 w = exp2(mul(log2(i), y));
result = wrap(uUNIT_SQUARED / w.unwrap());
}
}
/// @notice Raises x (a UD60x18 number) to the power y (an unsigned basic integer) using the well-known
/// algorithm "exponentiation by squaring".
///
/// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv18}.
/// - Returns `UNIT` for 0^0.
///
/// Requirements:
/// - The result must fit in UD60x18.
///
/// @param x The base as a UD60x18 number.
/// @param y The exponent as a uint256.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function powu(UD60x18 x, uint256 y) pure returns (UD60x18 result) {
// Calculate the first iteration of the loop in advance.
uint256 xUint = x.unwrap();
uint256 resultUint = y & 1 > 0 ? xUint : uUNIT;
// Equivalent to `for(y /= 2; y > 0; y /= 2)`.
for (y >>= 1; y > 0; y >>= 1) {
xUint = Common.mulDiv18(xUint, xUint);
// Equivalent to `y % 2 == 1`.
if (y & 1 > 0) {
resultUint = Common.mulDiv18(resultUint, xUint);
}
}
result = wrap(resultUint);
}
/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x must be less than `MAX_UD60x18 / UNIT`.
///
/// @param x The UD60x18 number for which to calculate the square root.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function sqrt(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
unchecked {
if (xUint > uMAX_UD60x18 / uUNIT) {
revert Errors.PRBMath_UD60x18_Sqrt_Overflow(x);
}
// Multiply x by `UNIT` to account for the factor of `UNIT` picked up when multiplying two UD60x18 numbers.
// In this case, the two numbers are both the square root.
result = wrap(Common.sqrt(xUint * uUNIT));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { wrap } from "./Casting.sol";
import { UD60x18 } from "./ValueType.sol";
/// @notice Implements the checked addition operation (+) in the UD60x18 type.
function add(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(x.unwrap() + y.unwrap());
}
/// @notice Implements the AND (&) bitwise operation in the UD60x18 type.
function and(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
result = wrap(x.unwrap() & bits);
}
/// @notice Implements the AND (&) bitwise operation in the UD60x18 type.
function and2(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(x.unwrap() & y.unwrap());
}
/// @notice Implements the equal operation (==) in the UD60x18 type.
function eq(UD60x18 x, UD60x18 y) pure returns (bool result) {
result = x.unwrap() == y.unwrap();
}
/// @notice Implements the greater than operation (>) in the UD60x18 type.
function gt(UD60x18 x, UD60x18 y) pure returns (bool result) {
result = x.unwrap() > y.unwrap();
}
/// @notice Implements the greater than or equal to operation (>=) in the UD60x18 type.
function gte(UD60x18 x, UD60x18 y) pure returns (bool result) {
result = x.unwrap() >= y.unwrap();
}
/// @notice Implements a zero comparison check function in the UD60x18 type.
function isZero(UD60x18 x) pure returns (bool result) {
// This wouldn't work if x could be negative.
result = x.unwrap() == 0;
}
/// @notice Implements the left shift operation (<<) in the UD60x18 type.
function lshift(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
result = wrap(x.unwrap() << bits);
}
/// @notice Implements the lower than operation (<) in the UD60x18 type.
function lt(UD60x18 x, UD60x18 y) pure returns (bool result) {
result = x.unwrap() < y.unwrap();
}
/// @notice Implements the lower than or equal to operation (<=) in the UD60x18 type.
function lte(UD60x18 x, UD60x18 y) pure returns (bool result) {
result = x.unwrap() <= y.unwrap();
}
/// @notice Implements the checked modulo operation (%) in the UD60x18 type.
function mod(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(x.unwrap() % y.unwrap());
}
/// @notice Implements the not equal operation (!=) in the UD60x18 type.
function neq(UD60x18 x, UD60x18 y) pure returns (bool result) {
result = x.unwrap() != y.unwrap();
}
/// @notice Implements the NOT (~) bitwise operation in the UD60x18 type.
function not(UD60x18 x) pure returns (UD60x18 result) {
result = wrap(~x.unwrap());
}
/// @notice Implements the OR (|) bitwise operation in the UD60x18 type.
function or(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(x.unwrap() | y.unwrap());
}
/// @notice Implements the right shift operation (>>) in the UD60x18 type.
function rshift(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
result = wrap(x.unwrap() >> bits);
}
/// @notice Implements the checked subtraction operation (-) in the UD60x18 type.
function sub(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(x.unwrap() - y.unwrap());
}
/// @notice Implements the unchecked addition operation (+) in the UD60x18 type.
function uncheckedAdd(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
unchecked {
result = wrap(x.unwrap() + y.unwrap());
}
}
/// @notice Implements the unchecked subtraction operation (-) in the UD60x18 type.
function uncheckedSub(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
unchecked {
result = wrap(x.unwrap() - y.unwrap());
}
}
/// @notice Implements the XOR (^) bitwise operation in the UD60x18 type.
function xor(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(x.unwrap() ^ y.unwrap());
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { UD60x18 } from "./ValueType.sol";
/// @notice Thrown when ceiling a number overflows UD60x18.
error PRBMath_UD60x18_Ceil_Overflow(UD60x18 x);
/// @notice Thrown when converting a basic integer to the fixed-point format overflows UD60x18.
error PRBMath_UD60x18_Convert_Overflow(uint256 x);
/// @notice Thrown when taking the natural exponent of a base greater than 133_084258667509499441.
error PRBMath_UD60x18_Exp_InputTooBig(UD60x18 x);
/// @notice Thrown when taking the binary exponent of a base greater than 192e18.
error PRBMath_UD60x18_Exp2_InputTooBig(UD60x18 x);
/// @notice Thrown when taking the geometric mean of two numbers and multiplying them overflows UD60x18.
error PRBMath_UD60x18_Gm_Overflow(UD60x18 x, UD60x18 y);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18.
error PRBMath_UD60x18_IntoSD1x18_Overflow(UD60x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD59x18.
error PRBMath_UD60x18_IntoSD59x18_Overflow(UD60x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18.
error PRBMath_UD60x18_IntoUD2x18_Overflow(UD60x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128.
error PRBMath_UD60x18_IntoUint128_Overflow(UD60x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint40.
error PRBMath_UD60x18_IntoUint40_Overflow(UD60x18 x);
/// @notice Thrown when taking the logarithm of a number less than 1.
error PRBMath_UD60x18_Log_InputTooSmall(UD60x18 x);
/// @notice Thrown when calculating the square root overflows UD60x18.
error PRBMath_UD60x18_Sqrt_Overflow(UD60x18 x);// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { uMAX_UD60x18, uUNIT } from "./Constants.sol";
import { PRBMath_UD60x18_Convert_Overflow } from "./Errors.sol";
import { UD60x18 } from "./ValueType.sol";
/// @notice Converts a UD60x18 number to a simple integer by dividing it by `UNIT`.
/// @dev The result is rounded toward zero.
/// @param x The UD60x18 number to convert.
/// @return result The same number in basic integer form.
function convert(UD60x18 x) pure returns (uint256 result) {
result = UD60x18.unwrap(x) / uUNIT;
}
/// @notice Converts a simple integer to UD60x18 by multiplying it by `UNIT`.
///
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UD60x18 / UNIT`.
///
/// @param x The basic integer to convert.
/// @param result The same number converted to UD60x18.
function convert(uint256 x) pure returns (UD60x18 result) {
if (x > uMAX_UD60x18 / uUNIT) {
revert PRBMath_UD60x18_Convert_Overflow(x);
}
unchecked {
result = UD60x18.wrap(x * uUNIT);
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { UD60x18 } from "./ValueType.sol";
// NOTICE: the "u" prefix stands for "unwrapped".
/// @dev Euler's number as a UD60x18 number.
UD60x18 constant E = UD60x18.wrap(2_718281828459045235);
/// @dev The maximum input permitted in {exp}.
uint256 constant uEXP_MAX_INPUT = 133_084258667509499440;
UD60x18 constant EXP_MAX_INPUT = UD60x18.wrap(uEXP_MAX_INPUT);
/// @dev The maximum input permitted in {exp2}.
uint256 constant uEXP2_MAX_INPUT = 192e18 - 1;
UD60x18 constant EXP2_MAX_INPUT = UD60x18.wrap(uEXP2_MAX_INPUT);
/// @dev Half the UNIT number.
uint256 constant uHALF_UNIT = 0.5e18;
UD60x18 constant HALF_UNIT = UD60x18.wrap(uHALF_UNIT);
/// @dev $log_2(10)$ as a UD60x18 number.
uint256 constant uLOG2_10 = 3_321928094887362347;
UD60x18 constant LOG2_10 = UD60x18.wrap(uLOG2_10);
/// @dev $log_2(e)$ as a UD60x18 number.
uint256 constant uLOG2_E = 1_442695040888963407;
UD60x18 constant LOG2_E = UD60x18.wrap(uLOG2_E);
/// @dev The maximum value a UD60x18 number can have.
uint256 constant uMAX_UD60x18 = 115792089237316195423570985008687907853269984665640564039457_584007913129639935;
UD60x18 constant MAX_UD60x18 = UD60x18.wrap(uMAX_UD60x18);
/// @dev The maximum whole value a UD60x18 number can have.
uint256 constant uMAX_WHOLE_UD60x18 = 115792089237316195423570985008687907853269984665640564039457_000000000000000000;
UD60x18 constant MAX_WHOLE_UD60x18 = UD60x18.wrap(uMAX_WHOLE_UD60x18);
/// @dev PI as a UD60x18 number.
UD60x18 constant PI = UD60x18.wrap(3_141592653589793238);
/// @dev The unit number, which gives the decimal precision of UD60x18.
uint256 constant uUNIT = 1e18;
UD60x18 constant UNIT = UD60x18.wrap(uUNIT);
/// @dev The unit number squared.
uint256 constant uUNIT_SQUARED = 1e36;
UD60x18 constant UNIT_SQUARED = UD60x18.wrap(uUNIT_SQUARED);
/// @dev Zero as a UD60x18 number.
UD60x18 constant ZERO = UD60x18.wrap(0);// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "./Errors.sol" as CastingErrors;
import { MAX_UINT128, MAX_UINT40 } from "../Common.sol";
import { uMAX_SD1x18 } from "../sd1x18/Constants.sol";
import { SD1x18 } from "../sd1x18/ValueType.sol";
import { uMAX_SD59x18 } from "../sd59x18/Constants.sol";
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { uMAX_UD2x18 } from "../ud2x18/Constants.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD60x18 } from "./ValueType.sol";
/// @notice Casts a UD60x18 number into SD1x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_SD1x18`.
function intoSD1x18(UD60x18 x) pure returns (SD1x18 result) {
uint256 xUint = UD60x18.unwrap(x);
if (xUint > uint256(int256(uMAX_SD1x18))) {
revert CastingErrors.PRBMath_UD60x18_IntoSD1x18_Overflow(x);
}
result = SD1x18.wrap(int64(uint64(xUint)));
}
/// @notice Casts a UD60x18 number into UD2x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_UD2x18`.
function intoUD2x18(UD60x18 x) pure returns (UD2x18 result) {
uint256 xUint = UD60x18.unwrap(x);
if (xUint > uMAX_UD2x18) {
revert CastingErrors.PRBMath_UD60x18_IntoUD2x18_Overflow(x);
}
result = UD2x18.wrap(uint64(xUint));
}
/// @notice Casts a UD60x18 number into SD59x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_SD59x18`.
function intoSD59x18(UD60x18 x) pure returns (SD59x18 result) {
uint256 xUint = UD60x18.unwrap(x);
if (xUint > uint256(uMAX_SD59x18)) {
revert CastingErrors.PRBMath_UD60x18_IntoSD59x18_Overflow(x);
}
result = SD59x18.wrap(int256(xUint));
}
/// @notice Casts a UD60x18 number into uint128.
/// @dev This is basically an alias for {unwrap}.
function intoUint256(UD60x18 x) pure returns (uint256 result) {
result = UD60x18.unwrap(x);
}
/// @notice Casts a UD60x18 number into uint128.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UINT128`.
function intoUint128(UD60x18 x) pure returns (uint128 result) {
uint256 xUint = UD60x18.unwrap(x);
if (xUint > MAX_UINT128) {
revert CastingErrors.PRBMath_UD60x18_IntoUint128_Overflow(x);
}
result = uint128(xUint);
}
/// @notice Casts a UD60x18 number into uint40.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(UD60x18 x) pure returns (uint40 result) {
uint256 xUint = UD60x18.unwrap(x);
if (xUint > MAX_UINT40) {
revert CastingErrors.PRBMath_UD60x18_IntoUint40_Overflow(x);
}
result = uint40(xUint);
}
/// @notice Alias for {wrap}.
function ud(uint256 x) pure returns (UD60x18 result) {
result = UD60x18.wrap(x);
}
/// @notice Alias for {wrap}.
function ud60x18(uint256 x) pure returns (UD60x18 result) {
result = UD60x18.wrap(x);
}
/// @notice Unwraps a UD60x18 number into uint256.
function unwrap(UD60x18 x) pure returns (uint256 result) {
result = UD60x18.unwrap(x);
}
/// @notice Wraps a uint256 number into the UD60x18 value type.
function wrap(uint256 x) pure returns (UD60x18 result) {
result = UD60x18.wrap(x);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
// Common.sol
//
// Common mathematical functions used in both SD59x18 and UD60x18. Note that these global functions do not
// always operate with SD59x18 and UD60x18 numbers.
/*//////////////////////////////////////////////////////////////////////////
CUSTOM ERRORS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when the resultant value in {mulDiv} overflows uint256.
error PRBMath_MulDiv_Overflow(uint256 x, uint256 y, uint256 denominator);
/// @notice Thrown when the resultant value in {mulDiv18} overflows uint256.
error PRBMath_MulDiv18_Overflow(uint256 x, uint256 y);
/// @notice Thrown when one of the inputs passed to {mulDivSigned} is `type(int256).min`.
error PRBMath_MulDivSigned_InputTooSmall();
/// @notice Thrown when the resultant value in {mulDivSigned} overflows int256.
error PRBMath_MulDivSigned_Overflow(int256 x, int256 y);
/*//////////////////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////////////////*/
/// @dev The maximum value a uint128 number can have.
uint128 constant MAX_UINT128 = type(uint128).max;
/// @dev The maximum value a uint40 number can have.
uint40 constant MAX_UINT40 = type(uint40).max;
/// @dev The unit number, which the decimal precision of the fixed-point types.
uint256 constant UNIT = 1e18;
/// @dev The unit number inverted mod 2^256.
uint256 constant UNIT_INVERSE = 78156646155174841979727994598816262306175212592076161876661_508869554232690281;
/// @dev The the largest power of two that divides the decimal value of `UNIT`. The logarithm of this value is the least significant
/// bit in the binary representation of `UNIT`.
uint256 constant UNIT_LPOTD = 262144;
/*//////////////////////////////////////////////////////////////////////////
FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Calculates the binary exponent of x using the binary fraction method.
/// @dev Has to use 192.64-bit fixed-point numbers. See https://ethereum.stackexchange.com/a/96594/24693.
/// @param x The exponent as an unsigned 192.64-bit fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function exp2(uint256 x) pure returns (uint256 result) {
unchecked {
// Start from 0.5 in the 192.64-bit fixed-point format.
result = 0x800000000000000000000000000000000000000000000000;
// The following logic multiplies the result by $\sqrt{2^{-i}}$ when the bit at position i is 1. Key points:
//
// 1. Intermediate results will not overflow, as the starting point is 2^191 and all magic factors are under 2^65.
// 2. The rationale for organizing the if statements into groups of 8 is gas savings. If the result of performing
// a bitwise AND operation between x and any value in the array [0x80; 0x40; 0x20; 0x10; 0x08; 0x04; 0x02; 0x01] is 1,
// we know that `x & 0xFF` is also 1.
if (x & 0xFF00000000000000 > 0) {
if (x & 0x8000000000000000 > 0) {
result = (result * 0x16A09E667F3BCC909) >> 64;
}
if (x & 0x4000000000000000 > 0) {
result = (result * 0x1306FE0A31B7152DF) >> 64;
}
if (x & 0x2000000000000000 > 0) {
result = (result * 0x1172B83C7D517ADCE) >> 64;
}
if (x & 0x1000000000000000 > 0) {
result = (result * 0x10B5586CF9890F62A) >> 64;
}
if (x & 0x800000000000000 > 0) {
result = (result * 0x1059B0D31585743AE) >> 64;
}
if (x & 0x400000000000000 > 0) {
result = (result * 0x102C9A3E778060EE7) >> 64;
}
if (x & 0x200000000000000 > 0) {
result = (result * 0x10163DA9FB33356D8) >> 64;
}
if (x & 0x100000000000000 > 0) {
result = (result * 0x100B1AFA5ABCBED61) >> 64;
}
}
if (x & 0xFF000000000000 > 0) {
if (x & 0x80000000000000 > 0) {
result = (result * 0x10058C86DA1C09EA2) >> 64;
}
if (x & 0x40000000000000 > 0) {
result = (result * 0x1002C605E2E8CEC50) >> 64;
}
if (x & 0x20000000000000 > 0) {
result = (result * 0x100162F3904051FA1) >> 64;
}
if (x & 0x10000000000000 > 0) {
result = (result * 0x1000B175EFFDC76BA) >> 64;
}
if (x & 0x8000000000000 > 0) {
result = (result * 0x100058BA01FB9F96D) >> 64;
}
if (x & 0x4000000000000 > 0) {
result = (result * 0x10002C5CC37DA9492) >> 64;
}
if (x & 0x2000000000000 > 0) {
result = (result * 0x1000162E525EE0547) >> 64;
}
if (x & 0x1000000000000 > 0) {
result = (result * 0x10000B17255775C04) >> 64;
}
}
if (x & 0xFF0000000000 > 0) {
if (x & 0x800000000000 > 0) {
result = (result * 0x1000058B91B5BC9AE) >> 64;
}
if (x & 0x400000000000 > 0) {
result = (result * 0x100002C5C89D5EC6D) >> 64;
}
if (x & 0x200000000000 > 0) {
result = (result * 0x10000162E43F4F831) >> 64;
}
if (x & 0x100000000000 > 0) {
result = (result * 0x100000B1721BCFC9A) >> 64;
}
if (x & 0x80000000000 > 0) {
result = (result * 0x10000058B90CF1E6E) >> 64;
}
if (x & 0x40000000000 > 0) {
result = (result * 0x1000002C5C863B73F) >> 64;
}
if (x & 0x20000000000 > 0) {
result = (result * 0x100000162E430E5A2) >> 64;
}
if (x & 0x10000000000 > 0) {
result = (result * 0x1000000B172183551) >> 64;
}
}
if (x & 0xFF00000000 > 0) {
if (x & 0x8000000000 > 0) {
result = (result * 0x100000058B90C0B49) >> 64;
}
if (x & 0x4000000000 > 0) {
result = (result * 0x10000002C5C8601CC) >> 64;
}
if (x & 0x2000000000 > 0) {
result = (result * 0x1000000162E42FFF0) >> 64;
}
if (x & 0x1000000000 > 0) {
result = (result * 0x10000000B17217FBB) >> 64;
}
if (x & 0x800000000 > 0) {
result = (result * 0x1000000058B90BFCE) >> 64;
}
if (x & 0x400000000 > 0) {
result = (result * 0x100000002C5C85FE3) >> 64;
}
if (x & 0x200000000 > 0) {
result = (result * 0x10000000162E42FF1) >> 64;
}
if (x & 0x100000000 > 0) {
result = (result * 0x100000000B17217F8) >> 64;
}
}
if (x & 0xFF000000 > 0) {
if (x & 0x80000000 > 0) {
result = (result * 0x10000000058B90BFC) >> 64;
}
if (x & 0x40000000 > 0) {
result = (result * 0x1000000002C5C85FE) >> 64;
}
if (x & 0x20000000 > 0) {
result = (result * 0x100000000162E42FF) >> 64;
}
if (x & 0x10000000 > 0) {
result = (result * 0x1000000000B17217F) >> 64;
}
if (x & 0x8000000 > 0) {
result = (result * 0x100000000058B90C0) >> 64;
}
if (x & 0x4000000 > 0) {
result = (result * 0x10000000002C5C860) >> 64;
}
if (x & 0x2000000 > 0) {
result = (result * 0x1000000000162E430) >> 64;
}
if (x & 0x1000000 > 0) {
result = (result * 0x10000000000B17218) >> 64;
}
}
if (x & 0xFF0000 > 0) {
if (x & 0x800000 > 0) {
result = (result * 0x1000000000058B90C) >> 64;
}
if (x & 0x400000 > 0) {
result = (result * 0x100000000002C5C86) >> 64;
}
if (x & 0x200000 > 0) {
result = (result * 0x10000000000162E43) >> 64;
}
if (x & 0x100000 > 0) {
result = (result * 0x100000000000B1721) >> 64;
}
if (x & 0x80000 > 0) {
result = (result * 0x10000000000058B91) >> 64;
}
if (x & 0x40000 > 0) {
result = (result * 0x1000000000002C5C8) >> 64;
}
if (x & 0x20000 > 0) {
result = (result * 0x100000000000162E4) >> 64;
}
if (x & 0x10000 > 0) {
result = (result * 0x1000000000000B172) >> 64;
}
}
if (x & 0xFF00 > 0) {
if (x & 0x8000 > 0) {
result = (result * 0x100000000000058B9) >> 64;
}
if (x & 0x4000 > 0) {
result = (result * 0x10000000000002C5D) >> 64;
}
if (x & 0x2000 > 0) {
result = (result * 0x1000000000000162E) >> 64;
}
if (x & 0x1000 > 0) {
result = (result * 0x10000000000000B17) >> 64;
}
if (x & 0x800 > 0) {
result = (result * 0x1000000000000058C) >> 64;
}
if (x & 0x400 > 0) {
result = (result * 0x100000000000002C6) >> 64;
}
if (x & 0x200 > 0) {
result = (result * 0x10000000000000163) >> 64;
}
if (x & 0x100 > 0) {
result = (result * 0x100000000000000B1) >> 64;
}
}
if (x & 0xFF > 0) {
if (x & 0x80 > 0) {
result = (result * 0x10000000000000059) >> 64;
}
if (x & 0x40 > 0) {
result = (result * 0x1000000000000002C) >> 64;
}
if (x & 0x20 > 0) {
result = (result * 0x10000000000000016) >> 64;
}
if (x & 0x10 > 0) {
result = (result * 0x1000000000000000B) >> 64;
}
if (x & 0x8 > 0) {
result = (result * 0x10000000000000006) >> 64;
}
if (x & 0x4 > 0) {
result = (result * 0x10000000000000003) >> 64;
}
if (x & 0x2 > 0) {
result = (result * 0x10000000000000001) >> 64;
}
if (x & 0x1 > 0) {
result = (result * 0x10000000000000001) >> 64;
}
}
// In the code snippet below, two operations are executed simultaneously:
//
// 1. The result is multiplied by $(2^n + 1)$, where $2^n$ represents the integer part, and the additional 1
// accounts for the initial guess of 0.5. This is achieved by subtracting from 191 instead of 192.
// 2. The result is then converted to an unsigned 60.18-decimal fixed-point format.
//
// The underlying logic is based on the relationship $2^{191-ip} = 2^{ip} / 2^{191}$, where $ip$ denotes the,
// integer part, $2^n$.
result *= UNIT;
result >>= (191 - (x >> 64));
}
}
/// @notice Finds the zero-based index of the first 1 in the binary representation of x.
///
/// @dev See the note on "msb" in this Wikipedia article: https://en.wikipedia.org/wiki/Find_first_set
///
/// Each step in this implementation is equivalent to this high-level code:
///
/// ```solidity
/// if (x >= 2 ** 128) {
/// x >>= 128;
/// result += 128;
/// }
/// ```
///
/// Where 128 is replaced with each respective power of two factor. See the full high-level implementation here:
/// https://gist.github.com/PaulRBerg/f932f8693f2733e30c4d479e8e980948
///
/// The Yul instructions used below are:
///
/// - "gt" is "greater than"
/// - "or" is the OR bitwise operator
/// - "shl" is "shift left"
/// - "shr" is "shift right"
///
/// @param x The uint256 number for which to find the index of the most significant bit.
/// @return result The index of the most significant bit as a uint256.
/// @custom:smtchecker abstract-function-nondet
function msb(uint256 x) pure returns (uint256 result) {
// 2^128
assembly ("memory-safe") {
let factor := shl(7, gt(x, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^64
assembly ("memory-safe") {
let factor := shl(6, gt(x, 0xFFFFFFFFFFFFFFFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^32
assembly ("memory-safe") {
let factor := shl(5, gt(x, 0xFFFFFFFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^16
assembly ("memory-safe") {
let factor := shl(4, gt(x, 0xFFFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^8
assembly ("memory-safe") {
let factor := shl(3, gt(x, 0xFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^4
assembly ("memory-safe") {
let factor := shl(2, gt(x, 0xF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^2
assembly ("memory-safe") {
let factor := shl(1, gt(x, 0x3))
x := shr(factor, x)
result := or(result, factor)
}
// 2^1
// No need to shift x any more.
assembly ("memory-safe") {
let factor := gt(x, 0x1)
result := or(result, factor)
}
}
/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev Credits to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - The denominator must not be zero.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as a uint256.
/// @param y The multiplier as a uint256.
/// @param denominator The divisor as a uint256.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function mulDiv(uint256 x, uint256 y, uint256 denominator) pure returns (uint256 result) {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512-bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly ("memory-safe") {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
unchecked {
return prod0 / denominator;
}
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (prod1 >= denominator) {
revert PRBMath_MulDiv_Overflow(x, y, denominator);
}
////////////////////////////////////////////////////////////////////////////
// 512 by 256 division
////////////////////////////////////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly ("memory-safe") {
// Compute remainder using the mulmod Yul instruction.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512-bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
unchecked {
// Calculate the largest power of two divisor of the denominator using the unary operator ~. This operation cannot overflow
// because the denominator cannot be zero at this point in the function execution. The result is always >= 1.
// For more detail, see https://cs.stackexchange.com/q/138556/92363.
uint256 lpotdod = denominator & (~denominator + 1);
uint256 flippedLpotdod;
assembly ("memory-safe") {
// Factor powers of two out of denominator.
denominator := div(denominator, lpotdod)
// Divide [prod1 prod0] by lpotdod.
prod0 := div(prod0, lpotdod)
// Get the flipped value `2^256 / lpotdod`. If the `lpotdod` is zero, the flipped value is one.
// `sub(0, lpotdod)` produces the two's complement version of `lpotdod`, which is equivalent to flipping all the bits.
// However, `div` interprets this value as an unsigned value: https://ethereum.stackexchange.com/q/147168/24693
flippedLpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * flippedLpotdod;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
}
}
/// @notice Calculates x*y÷1e18 with 512-bit precision.
///
/// @dev A variant of {mulDiv} with constant folding, i.e. in which the denominator is hard coded to 1e18.
///
/// Notes:
/// - The body is purposely left uncommented; to understand how this works, see the documentation in {mulDiv}.
/// - The result is rounded toward zero.
/// - We take as an axiom that the result cannot be `MAX_UINT256` when x and y solve the following system of equations:
///
/// $$
/// \begin{cases}
/// x * y = MAX\_UINT256 * UNIT \\
/// (x * y) \% UNIT \geq \frac{UNIT}{2}
/// \end{cases}
/// $$
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
/// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function mulDiv18(uint256 x, uint256 y) pure returns (uint256 result) {
uint256 prod0;
uint256 prod1;
assembly ("memory-safe") {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
unchecked {
return prod0 / UNIT;
}
}
if (prod1 >= UNIT) {
revert PRBMath_MulDiv18_Overflow(x, y);
}
uint256 remainder;
assembly ("memory-safe") {
remainder := mulmod(x, y, UNIT)
result :=
mul(
or(
div(sub(prod0, remainder), UNIT_LPOTD),
mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, UNIT_LPOTD), UNIT_LPOTD), 1))
),
UNIT_INVERSE
)
}
}
/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev This is an extension of {mulDiv} for signed numbers, which works by computing the signs and the absolute values separately.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - None of the inputs can be `type(int256).min`.
/// - The result must fit in int256.
///
/// @param x The multiplicand as an int256.
/// @param y The multiplier as an int256.
/// @param denominator The divisor as an int256.
/// @return result The result as an int256.
/// @custom:smtchecker abstract-function-nondet
function mulDivSigned(int256 x, int256 y, int256 denominator) pure returns (int256 result) {
if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
revert PRBMath_MulDivSigned_InputTooSmall();
}
// Get hold of the absolute values of x, y and the denominator.
uint256 xAbs;
uint256 yAbs;
uint256 dAbs;
unchecked {
xAbs = x < 0 ? uint256(-x) : uint256(x);
yAbs = y < 0 ? uint256(-y) : uint256(y);
dAbs = denominator < 0 ? uint256(-denominator) : uint256(denominator);
}
// Compute the absolute value of x*y÷denominator. The result must fit in int256.
uint256 resultAbs = mulDiv(xAbs, yAbs, dAbs);
if (resultAbs > uint256(type(int256).max)) {
revert PRBMath_MulDivSigned_Overflow(x, y);
}
// Get the signs of x, y and the denominator.
uint256 sx;
uint256 sy;
uint256 sd;
assembly ("memory-safe") {
// "sgt" is the "signed greater than" assembly instruction and "sub(0,1)" is -1 in two's complement.
sx := sgt(x, sub(0, 1))
sy := sgt(y, sub(0, 1))
sd := sgt(denominator, sub(0, 1))
}
// XOR over sx, sy and sd. What this does is to check whether there are 1 or 3 negative signs in the inputs.
// If there are, the result should be negative. Otherwise, it should be positive.
unchecked {
result = sx ^ sy ^ sd == 0 ? -int256(resultAbs) : int256(resultAbs);
}
}
/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - If x is not a perfect square, the result is rounded down.
/// - Credits to OpenZeppelin for the explanations in comments below.
///
/// @param x The uint256 number for which to calculate the square root.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function sqrt(uint256 x) pure returns (uint256 result) {
if (x == 0) {
return 0;
}
// For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.
//
// We know that the "msb" (most significant bit) of x is a power of 2 such that we have:
//
// $$
// msb(x) <= x <= 2*msb(x)$
// $$
//
// We write $msb(x)$ as $2^k$, and we get:
//
// $$
// k = log_2(x)
// $$
//
// Thus, we can write the initial inequality as:
//
// $$
// 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\
// sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\
// 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}
// $$
//
// Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.
uint256 xAux = uint256(x);
result = 1;
if (xAux >= 2 ** 128) {
xAux >>= 128;
result <<= 64;
}
if (xAux >= 2 ** 64) {
xAux >>= 64;
result <<= 32;
}
if (xAux >= 2 ** 32) {
xAux >>= 32;
result <<= 16;
}
if (xAux >= 2 ** 16) {
xAux >>= 16;
result <<= 8;
}
if (xAux >= 2 ** 8) {
xAux >>= 8;
result <<= 4;
}
if (xAux >= 2 ** 4) {
xAux >>= 4;
result <<= 2;
}
if (xAux >= 2 ** 2) {
result <<= 1;
}
// At this point, `result` is an estimation with at least one bit of precision. We know the true value has at
// most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision
// doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of
// precision into the expected uint128 result.
unchecked {
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
// If x is not a perfect square, round the result toward zero.
uint256 roundedResult = x / result;
if (result >= roundedResult) {
result = roundedResult;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "./Casting.sol" as Casting;
/// @notice The unsigned 2.18-decimal fixed-point number representation, which can have up to 2 digits and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity
/// type uint64. This is useful when end users want to use uint64 to save gas, e.g. with tight variable packing in contract
/// storage.
type UD2x18 is uint64;
/*//////////////////////////////////////////////////////////////////////////
CASTING
//////////////////////////////////////////////////////////////////////////*/
using {
Casting.intoSD1x18,
Casting.intoSD59x18,
Casting.intoUD60x18,
Casting.intoUint256,
Casting.intoUint128,
Casting.intoUint40,
Casting.unwrap
} for UD2x18 global;// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { UD2x18 } from "./ValueType.sol";
/// @dev Euler's number as a UD2x18 number.
UD2x18 constant E = UD2x18.wrap(2_718281828459045235);
/// @dev The maximum value a UD2x18 number can have.
uint64 constant uMAX_UD2x18 = 18_446744073709551615;
UD2x18 constant MAX_UD2x18 = UD2x18.wrap(uMAX_UD2x18);
/// @dev PI as a UD2x18 number.
UD2x18 constant PI = UD2x18.wrap(3_141592653589793238);
/// @dev The unit number, which gives the decimal precision of UD2x18.
UD2x18 constant UNIT = UD2x18.wrap(1e18);
uint64 constant uUNIT = 1e18;// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "./Casting.sol" as Casting;
import "./Helpers.sol" as Helpers;
import "./Math.sol" as Math;
/// @notice The signed 59.18-decimal fixed-point number representation, which can have up to 59 digits and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity
/// type int256.
type SD59x18 is int256;
/*//////////////////////////////////////////////////////////////////////////
CASTING
//////////////////////////////////////////////////////////////////////////*/
using {
Casting.intoInt256,
Casting.intoSD1x18,
Casting.intoUD2x18,
Casting.intoUD60x18,
Casting.intoUint256,
Casting.intoUint128,
Casting.intoUint40,
Casting.unwrap
} for SD59x18 global;
/*//////////////////////////////////////////////////////////////////////////
MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
using {
Math.abs,
Math.avg,
Math.ceil,
Math.div,
Math.exp,
Math.exp2,
Math.floor,
Math.frac,
Math.gm,
Math.inv,
Math.log10,
Math.log2,
Math.ln,
Math.mul,
Math.pow,
Math.powu,
Math.sqrt
} for SD59x18 global;
/*//////////////////////////////////////////////////////////////////////////
HELPER FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
using {
Helpers.add,
Helpers.and,
Helpers.eq,
Helpers.gt,
Helpers.gte,
Helpers.isZero,
Helpers.lshift,
Helpers.lt,
Helpers.lte,
Helpers.mod,
Helpers.neq,
Helpers.not,
Helpers.or,
Helpers.rshift,
Helpers.sub,
Helpers.uncheckedAdd,
Helpers.uncheckedSub,
Helpers.uncheckedUnary,
Helpers.xor
} for SD59x18 global;
/*//////////////////////////////////////////////////////////////////////////
OPERATORS
//////////////////////////////////////////////////////////////////////////*/
// The global "using for" directive makes it possible to use these operators on the SD59x18 type.
using {
Helpers.add as +,
Helpers.and2 as &,
Math.div as /,
Helpers.eq as ==,
Helpers.gt as >,
Helpers.gte as >=,
Helpers.lt as <,
Helpers.lte as <=,
Helpers.mod as %,
Math.mul as *,
Helpers.neq as !=,
Helpers.not as ~,
Helpers.or as |,
Helpers.sub as -,
Helpers.unary as -,
Helpers.xor as ^
} for SD59x18 global;// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { SD59x18 } from "./ValueType.sol";
// NOTICE: the "u" prefix stands for "unwrapped".
/// @dev Euler's number as an SD59x18 number.
SD59x18 constant E = SD59x18.wrap(2_718281828459045235);
/// @dev The maximum input permitted in {exp}.
int256 constant uEXP_MAX_INPUT = 133_084258667509499440;
SD59x18 constant EXP_MAX_INPUT = SD59x18.wrap(uEXP_MAX_INPUT);
/// @dev Any value less than this returns 0 in {exp}.
int256 constant uEXP_MIN_THRESHOLD = -41_446531673892822322;
SD59x18 constant EXP_MIN_THRESHOLD = SD59x18.wrap(uEXP_MIN_THRESHOLD);
/// @dev The maximum input permitted in {exp2}.
int256 constant uEXP2_MAX_INPUT = 192e18 - 1;
SD59x18 constant EXP2_MAX_INPUT = SD59x18.wrap(uEXP2_MAX_INPUT);
/// @dev Any value less than this returns 0 in {exp2}.
int256 constant uEXP2_MIN_THRESHOLD = -59_794705707972522261;
SD59x18 constant EXP2_MIN_THRESHOLD = SD59x18.wrap(uEXP2_MIN_THRESHOLD);
/// @dev Half the UNIT number.
int256 constant uHALF_UNIT = 0.5e18;
SD59x18 constant HALF_UNIT = SD59x18.wrap(uHALF_UNIT);
/// @dev $log_2(10)$ as an SD59x18 number.
int256 constant uLOG2_10 = 3_321928094887362347;
SD59x18 constant LOG2_10 = SD59x18.wrap(uLOG2_10);
/// @dev $log_2(e)$ as an SD59x18 number.
int256 constant uLOG2_E = 1_442695040888963407;
SD59x18 constant LOG2_E = SD59x18.wrap(uLOG2_E);
/// @dev The maximum value an SD59x18 number can have.
int256 constant uMAX_SD59x18 = 57896044618658097711785492504343953926634992332820282019728_792003956564819967;
SD59x18 constant MAX_SD59x18 = SD59x18.wrap(uMAX_SD59x18);
/// @dev The maximum whole value an SD59x18 number can have.
int256 constant uMAX_WHOLE_SD59x18 = 57896044618658097711785492504343953926634992332820282019728_000000000000000000;
SD59x18 constant MAX_WHOLE_SD59x18 = SD59x18.wrap(uMAX_WHOLE_SD59x18);
/// @dev The minimum value an SD59x18 number can have.
int256 constant uMIN_SD59x18 = -57896044618658097711785492504343953926634992332820282019728_792003956564819968;
SD59x18 constant MIN_SD59x18 = SD59x18.wrap(uMIN_SD59x18);
/// @dev The minimum whole value an SD59x18 number can have.
int256 constant uMIN_WHOLE_SD59x18 = -57896044618658097711785492504343953926634992332820282019728_000000000000000000;
SD59x18 constant MIN_WHOLE_SD59x18 = SD59x18.wrap(uMIN_WHOLE_SD59x18);
/// @dev PI as an SD59x18 number.
SD59x18 constant PI = SD59x18.wrap(3_141592653589793238);
/// @dev The unit number, which gives the decimal precision of SD59x18.
int256 constant uUNIT = 1e18;
SD59x18 constant UNIT = SD59x18.wrap(1e18);
/// @dev The unit number squared.
int256 constant uUNIT_SQUARED = 1e36;
SD59x18 constant UNIT_SQUARED = SD59x18.wrap(uUNIT_SQUARED);
/// @dev Zero as an SD59x18 number.
SD59x18 constant ZERO = SD59x18.wrap(0);// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "./Casting.sol" as Casting;
/// @notice The signed 1.18-decimal fixed-point number representation, which can have up to 1 digit and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity
/// type int64. This is useful when end users want to use int64 to save gas, e.g. with tight variable packing in contract
/// storage.
type SD1x18 is int64;
/*//////////////////////////////////////////////////////////////////////////
CASTING
//////////////////////////////////////////////////////////////////////////*/
using {
Casting.intoSD59x18,
Casting.intoUD2x18,
Casting.intoUD60x18,
Casting.intoUint256,
Casting.intoUint128,
Casting.intoUint40,
Casting.unwrap
} for SD1x18 global;// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { SD1x18 } from "./ValueType.sol";
/// @dev Euler's number as an SD1x18 number.
SD1x18 constant E = SD1x18.wrap(2_718281828459045235);
/// @dev The maximum value an SD1x18 number can have.
int64 constant uMAX_SD1x18 = 9_223372036854775807;
SD1x18 constant MAX_SD1x18 = SD1x18.wrap(uMAX_SD1x18);
/// @dev The maximum value an SD1x18 number can have.
int64 constant uMIN_SD1x18 = -9_223372036854775808;
SD1x18 constant MIN_SD1x18 = SD1x18.wrap(uMIN_SD1x18);
/// @dev PI as an SD1x18 number.
SD1x18 constant PI = SD1x18.wrap(3_141592653589793238);
/// @dev The unit number, which gives the decimal precision of SD1x18.
SD1x18 constant UNIT = SD1x18.wrap(1e18);
int64 constant uUNIT = 1e18;// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import { uMAX_SD1x18 } from "../sd1x18/Constants.sol";
import { SD1x18 } from "../sd1x18/ValueType.sol";
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";
import { UD2x18 } from "./ValueType.sol";
/// @notice Casts a UD2x18 number into SD1x18.
/// - x must be less than or equal to `uMAX_SD1x18`.
function intoSD1x18(UD2x18 x) pure returns (SD1x18 result) {
uint64 xUint = UD2x18.unwrap(x);
if (xUint > uint64(uMAX_SD1x18)) {
revert Errors.PRBMath_UD2x18_IntoSD1x18_Overflow(x);
}
result = SD1x18.wrap(int64(xUint));
}
/// @notice Casts a UD2x18 number into SD59x18.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of SD59x18.
function intoSD59x18(UD2x18 x) pure returns (SD59x18 result) {
result = SD59x18.wrap(int256(uint256(UD2x18.unwrap(x))));
}
/// @notice Casts a UD2x18 number into UD60x18.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of UD60x18.
function intoUD60x18(UD2x18 x) pure returns (UD60x18 result) {
result = UD60x18.wrap(UD2x18.unwrap(x));
}
/// @notice Casts a UD2x18 number into uint128.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of uint128.
function intoUint128(UD2x18 x) pure returns (uint128 result) {
result = uint128(UD2x18.unwrap(x));
}
/// @notice Casts a UD2x18 number into uint256.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of uint256.
function intoUint256(UD2x18 x) pure returns (uint256 result) {
result = uint256(UD2x18.unwrap(x));
}
/// @notice Casts a UD2x18 number into uint40.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(UD2x18 x) pure returns (uint40 result) {
uint64 xUint = UD2x18.unwrap(x);
if (xUint > uint64(Common.MAX_UINT40)) {
revert Errors.PRBMath_UD2x18_IntoUint40_Overflow(x);
}
result = uint40(xUint);
}
/// @notice Alias for {wrap}.
function ud2x18(uint64 x) pure returns (UD2x18 result) {
result = UD2x18.wrap(x);
}
/// @notice Unwrap a UD2x18 number into uint64.
function unwrap(UD2x18 x) pure returns (uint64 result) {
result = UD2x18.unwrap(x);
}
/// @notice Wraps a uint64 number into UD2x18.
function wrap(uint64 x) pure returns (UD2x18 result) {
result = UD2x18.wrap(x);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import {
uEXP_MAX_INPUT,
uEXP2_MAX_INPUT,
uEXP_MIN_THRESHOLD,
uEXP2_MIN_THRESHOLD,
uHALF_UNIT,
uLOG2_10,
uLOG2_E,
uMAX_SD59x18,
uMAX_WHOLE_SD59x18,
uMIN_SD59x18,
uMIN_WHOLE_SD59x18,
UNIT,
uUNIT,
uUNIT_SQUARED,
ZERO
} from "./Constants.sol";
import { wrap } from "./Helpers.sol";
import { SD59x18 } from "./ValueType.sol";
/// @notice Calculates the absolute value of x.
///
/// @dev Requirements:
/// - x must be greater than `MIN_SD59x18`.
///
/// @param x The SD59x18 number for which to calculate the absolute value.
/// @param result The absolute value of x as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function abs(SD59x18 x) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
if (xInt == uMIN_SD59x18) {
revert Errors.PRBMath_SD59x18_Abs_MinSD59x18();
}
result = xInt < 0 ? wrap(-xInt) : x;
}
/// @notice Calculates the arithmetic average of x and y.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// @param x The first operand as an SD59x18 number.
/// @param y The second operand as an SD59x18 number.
/// @return result The arithmetic average as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function avg(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
int256 yInt = y.unwrap();
unchecked {
// This operation is equivalent to `x / 2 + y / 2`, and it can never overflow.
int256 sum = (xInt >> 1) + (yInt >> 1);
if (sum < 0) {
// If at least one of x and y is odd, add 1 to the result, because shifting negative numbers to the right
// rounds toward negative infinity. The right part is equivalent to `sum + (x % 2 == 1 || y % 2 == 1)`.
assembly ("memory-safe") {
result := add(sum, and(or(xInt, yInt), 1))
}
} else {
// Add 1 if both x and y are odd to account for the double 0.5 remainder truncated after shifting.
result = wrap(sum + (xInt & yInt & 1));
}
}
}
/// @notice Yields the smallest whole number greater than or equal to x.
///
/// @dev Optimized for fractional value inputs, because every whole value has (1e18 - 1) fractional counterparts.
/// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x must be less than or equal to `MAX_WHOLE_SD59x18`.
///
/// @param x The SD59x18 number to ceil.
/// @param result The smallest whole number greater than or equal to x, as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function ceil(SD59x18 x) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
if (xInt > uMAX_WHOLE_SD59x18) {
revert Errors.PRBMath_SD59x18_Ceil_Overflow(x);
}
int256 remainder = xInt % uUNIT;
if (remainder == 0) {
result = x;
} else {
unchecked {
// Solidity uses C fmod style, which returns a modulus with the same sign as x.
int256 resultInt = xInt - remainder;
if (xInt > 0) {
resultInt += uUNIT;
}
result = wrap(resultInt);
}
}
}
/// @notice Divides two SD59x18 numbers, returning a new SD59x18 number.
///
/// @dev This is an extension of {Common.mulDiv} for signed numbers, which works by computing the signs and the absolute
/// values separately.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
/// - The result is rounded toward zero.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
/// - None of the inputs can be `MIN_SD59x18`.
/// - The denominator must not be zero.
/// - The result must fit in SD59x18.
///
/// @param x The numerator as an SD59x18 number.
/// @param y The denominator as an SD59x18 number.
/// @param result The quotient as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function div(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
int256 yInt = y.unwrap();
if (xInt == uMIN_SD59x18 || yInt == uMIN_SD59x18) {
revert Errors.PRBMath_SD59x18_Div_InputTooSmall();
}
// Get hold of the absolute values of x and y.
uint256 xAbs;
uint256 yAbs;
unchecked {
xAbs = xInt < 0 ? uint256(-xInt) : uint256(xInt);
yAbs = yInt < 0 ? uint256(-yInt) : uint256(yInt);
}
// Compute the absolute value (x*UNIT÷y). The resulting value must fit in SD59x18.
uint256 resultAbs = Common.mulDiv(xAbs, uint256(uUNIT), yAbs);
if (resultAbs > uint256(uMAX_SD59x18)) {
revert Errors.PRBMath_SD59x18_Div_Overflow(x, y);
}
// Check if x and y have the same sign using two's complement representation. The left-most bit represents the sign (1 for
// negative, 0 for positive or zero).
bool sameSign = (xInt ^ yInt) > -1;
// If the inputs have the same sign, the result should be positive. Otherwise, it should be negative.
unchecked {
result = wrap(sameSign ? int256(resultAbs) : -int256(resultAbs));
}
}
/// @notice Calculates the natural exponent of x using the following formula:
///
/// $$
/// e^x = 2^{x * log_2{e}}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {exp2}.
///
/// Requirements:
/// - Refer to the requirements in {exp2}.
/// - x must be less than 133_084258667509499441.
///
/// @param x The exponent as an SD59x18 number.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp(SD59x18 x) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
// Any input less than the threshold returns zero.
// This check also prevents an overflow for very small numbers.
if (xInt < uEXP_MIN_THRESHOLD) {
return ZERO;
}
// This check prevents values greater than 192e18 from being passed to {exp2}.
if (xInt > uEXP_MAX_INPUT) {
revert Errors.PRBMath_SD59x18_Exp_InputTooBig(x);
}
unchecked {
// Inline the fixed-point multiplication to save gas.
int256 doubleUnitProduct = xInt * uLOG2_E;
result = exp2(wrap(doubleUnitProduct / uUNIT));
}
}
/// @notice Calculates the binary exponent of x using the binary fraction method using the following formula:
///
/// $$
/// 2^{-x} = \frac{1}{2^x}
/// $$
///
/// @dev See https://ethereum.stackexchange.com/q/79903/24693.
///
/// Notes:
/// - If x is less than -59_794705707972522261, the result is zero.
///
/// Requirements:
/// - x must be less than 192e18.
/// - The result must fit in SD59x18.
///
/// @param x The exponent as an SD59x18 number.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp2(SD59x18 x) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
if (xInt < 0) {
// The inverse of any number less than the threshold is truncated to zero.
if (xInt < uEXP2_MIN_THRESHOLD) {
return ZERO;
}
unchecked {
// Inline the fixed-point inversion to save gas.
result = wrap(uUNIT_SQUARED / exp2(wrap(-xInt)).unwrap());
}
} else {
// Numbers greater than or equal to 192e18 don't fit in the 192.64-bit format.
if (xInt > uEXP2_MAX_INPUT) {
revert Errors.PRBMath_SD59x18_Exp2_InputTooBig(x);
}
unchecked {
// Convert x to the 192.64-bit fixed-point format.
uint256 x_192x64 = uint256((xInt << 64) / uUNIT);
// It is safe to cast the result to int256 due to the checks above.
result = wrap(int256(Common.exp2(x_192x64)));
}
}
}
/// @notice Yields the greatest whole number less than or equal to x.
///
/// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional
/// counterparts. See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x must be greater than or equal to `MIN_WHOLE_SD59x18`.
///
/// @param x The SD59x18 number to floor.
/// @param result The greatest whole number less than or equal to x, as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function floor(SD59x18 x) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
if (xInt < uMIN_WHOLE_SD59x18) {
revert Errors.PRBMath_SD59x18_Floor_Underflow(x);
}
int256 remainder = xInt % uUNIT;
if (remainder == 0) {
result = x;
} else {
unchecked {
// Solidity uses C fmod style, which returns a modulus with the same sign as x.
int256 resultInt = xInt - remainder;
if (xInt < 0) {
resultInt -= uUNIT;
}
result = wrap(resultInt);
}
}
}
/// @notice Yields the excess beyond the floor of x for positive numbers and the part of the number to the right.
/// of the radix point for negative numbers.
/// @dev Based on the odd function definition. https://en.wikipedia.org/wiki/Fractional_part
/// @param x The SD59x18 number to get the fractional part of.
/// @param result The fractional part of x as an SD59x18 number.
function frac(SD59x18 x) pure returns (SD59x18 result) {
result = wrap(x.unwrap() % uUNIT);
}
/// @notice Calculates the geometric mean of x and y, i.e. $\sqrt{x * y}$.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x * y must fit in SD59x18.
/// - x * y must not be negative, since complex numbers are not supported.
///
/// @param x The first operand as an SD59x18 number.
/// @param y The second operand as an SD59x18 number.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function gm(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
int256 yInt = y.unwrap();
if (xInt == 0 || yInt == 0) {
return ZERO;
}
unchecked {
// Equivalent to `xy / x != y`. Checking for overflow this way is faster than letting Solidity do it.
int256 xyInt = xInt * yInt;
if (xyInt / xInt != yInt) {
revert Errors.PRBMath_SD59x18_Gm_Overflow(x, y);
}
// The product must not be negative, since complex numbers are not supported.
if (xyInt < 0) {
revert Errors.PRBMath_SD59x18_Gm_NegativeProduct(x, y);
}
// We don't need to multiply the result by `UNIT` here because the x*y product picked up a factor of `UNIT`
// during multiplication. See the comments in {Common.sqrt}.
uint256 resultUint = Common.sqrt(uint256(xyInt));
result = wrap(int256(resultUint));
}
}
/// @notice Calculates the inverse of x.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x must not be zero.
///
/// @param x The SD59x18 number for which to calculate the inverse.
/// @return result The inverse as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function inv(SD59x18 x) pure returns (SD59x18 result) {
result = wrap(uUNIT_SQUARED / x.unwrap());
}
/// @notice Calculates the natural logarithm of x using the following formula:
///
/// $$
/// ln{x} = log_2{x} / log_2{e}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
/// - The precision isn't sufficiently fine-grained to return exactly `UNIT` when the input is `E`.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The SD59x18 number for which to calculate the natural logarithm.
/// @return result The natural logarithm as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function ln(SD59x18 x) pure returns (SD59x18 result) {
// Inline the fixed-point multiplication to save gas. This is overflow-safe because the maximum value that
// {log2} can return is ~195_205294292027477728.
result = wrap(log2(x).unwrap() * uUNIT / uLOG2_E);
}
/// @notice Calculates the common logarithm of x using the following formula:
///
/// $$
/// log_{10}{x} = log_2{x} / log_2{10}
/// $$
///
/// However, if x is an exact power of ten, a hard coded value is returned.
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The SD59x18 number for which to calculate the common logarithm.
/// @return result The common logarithm as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function log10(SD59x18 x) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
if (xInt < 0) {
revert Errors.PRBMath_SD59x18_Log_InputTooSmall(x);
}
// Note that the `mul` in this block is the standard multiplication operation, not {SD59x18.mul}.
// prettier-ignore
assembly ("memory-safe") {
switch x
case 1 { result := mul(uUNIT, sub(0, 18)) }
case 10 { result := mul(uUNIT, sub(1, 18)) }
case 100 { result := mul(uUNIT, sub(2, 18)) }
case 1000 { result := mul(uUNIT, sub(3, 18)) }
case 10000 { result := mul(uUNIT, sub(4, 18)) }
case 100000 { result := mul(uUNIT, sub(5, 18)) }
case 1000000 { result := mul(uUNIT, sub(6, 18)) }
case 10000000 { result := mul(uUNIT, sub(7, 18)) }
case 100000000 { result := mul(uUNIT, sub(8, 18)) }
case 1000000000 { result := mul(uUNIT, sub(9, 18)) }
case 10000000000 { result := mul(uUNIT, sub(10, 18)) }
case 100000000000 { result := mul(uUNIT, sub(11, 18)) }
case 1000000000000 { result := mul(uUNIT, sub(12, 18)) }
case 10000000000000 { result := mul(uUNIT, sub(13, 18)) }
case 100000000000000 { result := mul(uUNIT, sub(14, 18)) }
case 1000000000000000 { result := mul(uUNIT, sub(15, 18)) }
case 10000000000000000 { result := mul(uUNIT, sub(16, 18)) }
case 100000000000000000 { result := mul(uUNIT, sub(17, 18)) }
case 1000000000000000000 { result := 0 }
case 10000000000000000000 { result := uUNIT }
case 100000000000000000000 { result := mul(uUNIT, 2) }
case 1000000000000000000000 { result := mul(uUNIT, 3) }
case 10000000000000000000000 { result := mul(uUNIT, 4) }
case 100000000000000000000000 { result := mul(uUNIT, 5) }
case 1000000000000000000000000 { result := mul(uUNIT, 6) }
case 10000000000000000000000000 { result := mul(uUNIT, 7) }
case 100000000000000000000000000 { result := mul(uUNIT, 8) }
case 1000000000000000000000000000 { result := mul(uUNIT, 9) }
case 10000000000000000000000000000 { result := mul(uUNIT, 10) }
case 100000000000000000000000000000 { result := mul(uUNIT, 11) }
case 1000000000000000000000000000000 { result := mul(uUNIT, 12) }
case 10000000000000000000000000000000 { result := mul(uUNIT, 13) }
case 100000000000000000000000000000000 { result := mul(uUNIT, 14) }
case 1000000000000000000000000000000000 { result := mul(uUNIT, 15) }
case 10000000000000000000000000000000000 { result := mul(uUNIT, 16) }
case 100000000000000000000000000000000000 { result := mul(uUNIT, 17) }
case 1000000000000000000000000000000000000 { result := mul(uUNIT, 18) }
case 10000000000000000000000000000000000000 { result := mul(uUNIT, 19) }
case 100000000000000000000000000000000000000 { result := mul(uUNIT, 20) }
case 1000000000000000000000000000000000000000 { result := mul(uUNIT, 21) }
case 10000000000000000000000000000000000000000 { result := mul(uUNIT, 22) }
case 100000000000000000000000000000000000000000 { result := mul(uUNIT, 23) }
case 1000000000000000000000000000000000000000000 { result := mul(uUNIT, 24) }
case 10000000000000000000000000000000000000000000 { result := mul(uUNIT, 25) }
case 100000000000000000000000000000000000000000000 { result := mul(uUNIT, 26) }
case 1000000000000000000000000000000000000000000000 { result := mul(uUNIT, 27) }
case 10000000000000000000000000000000000000000000000 { result := mul(uUNIT, 28) }
case 100000000000000000000000000000000000000000000000 { result := mul(uUNIT, 29) }
case 1000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 30) }
case 10000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 31) }
case 100000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 32) }
case 1000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 33) }
case 10000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 34) }
case 100000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 35) }
case 1000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 36) }
case 10000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 37) }
case 100000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 38) }
case 1000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 39) }
case 10000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 40) }
case 100000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 41) }
case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 42) }
case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 43) }
case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 44) }
case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 45) }
case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 46) }
case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 47) }
case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 48) }
case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 49) }
case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 50) }
case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 51) }
case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 52) }
case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 53) }
case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 54) }
case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 55) }
case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 56) }
case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 57) }
case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 58) }
default { result := uMAX_SD59x18 }
}
if (result.unwrap() == uMAX_SD59x18) {
unchecked {
// Inline the fixed-point division to save gas.
result = wrap(log2(x).unwrap() * uUNIT / uLOG2_10);
}
}
}
/// @notice Calculates the binary logarithm of x using the iterative approximation algorithm:
///
/// $$
/// log_2{x} = n + log_2{y}, \text{ where } y = x*2^{-n}, \ y \in [1, 2)
/// $$
///
/// For $0 \leq x \lt 1$, the input is inverted:
///
/// $$
/// log_2{x} = -log_2{\frac{1}{x}}
/// $$
///
/// @dev See https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation.
///
/// Notes:
/// - Due to the lossy precision of the iterative approximation, the results are not perfectly accurate to the last decimal.
///
/// Requirements:
/// - x must be greater than zero.
///
/// @param x The SD59x18 number for which to calculate the binary logarithm.
/// @return result The binary logarithm as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function log2(SD59x18 x) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
if (xInt <= 0) {
revert Errors.PRBMath_SD59x18_Log_InputTooSmall(x);
}
unchecked {
int256 sign;
if (xInt >= uUNIT) {
sign = 1;
} else {
sign = -1;
// Inline the fixed-point inversion to save gas.
xInt = uUNIT_SQUARED / xInt;
}
// Calculate the integer part of the logarithm.
uint256 n = Common.msb(uint256(xInt / uUNIT));
// This is the integer part of the logarithm as an SD59x18 number. The operation can't overflow
// because n is at most 255, `UNIT` is 1e18, and the sign is either 1 or -1.
int256 resultInt = int256(n) * uUNIT;
// Calculate $y = x * 2^{-n}$.
int256 y = xInt >> n;
// If y is the unit number, the fractional part is zero.
if (y == uUNIT) {
return wrap(resultInt * sign);
}
// Calculate the fractional part via the iterative approximation.
// The `delta >>= 1` part is equivalent to `delta /= 2`, but shifting bits is more gas efficient.
int256 DOUBLE_UNIT = 2e18;
for (int256 delta = uHALF_UNIT; delta > 0; delta >>= 1) {
y = (y * y) / uUNIT;
// Is y^2 >= 2e18 and so in the range [2e18, 4e18)?
if (y >= DOUBLE_UNIT) {
// Add the 2^{-m} factor to the logarithm.
resultInt = resultInt + delta;
// Halve y, which corresponds to z/2 in the Wikipedia article.
y >>= 1;
}
}
resultInt *= sign;
result = wrap(resultInt);
}
}
/// @notice Multiplies two SD59x18 numbers together, returning a new SD59x18 number.
///
/// @dev Notes:
/// - Refer to the notes in {Common.mulDiv18}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv18}.
/// - None of the inputs can be `MIN_SD59x18`.
/// - The result must fit in SD59x18.
///
/// @param x The multiplicand as an SD59x18 number.
/// @param y The multiplier as an SD59x18 number.
/// @return result The product as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function mul(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
int256 yInt = y.unwrap();
if (xInt == uMIN_SD59x18 || yInt == uMIN_SD59x18) {
revert Errors.PRBMath_SD59x18_Mul_InputTooSmall();
}
// Get hold of the absolute values of x and y.
uint256 xAbs;
uint256 yAbs;
unchecked {
xAbs = xInt < 0 ? uint256(-xInt) : uint256(xInt);
yAbs = yInt < 0 ? uint256(-yInt) : uint256(yInt);
}
// Compute the absolute value (x*y÷UNIT). The resulting value must fit in SD59x18.
uint256 resultAbs = Common.mulDiv18(xAbs, yAbs);
if (resultAbs > uint256(uMAX_SD59x18)) {
revert Errors.PRBMath_SD59x18_Mul_Overflow(x, y);
}
// Check if x and y have the same sign using two's complement representation. The left-most bit represents the sign (1 for
// negative, 0 for positive or zero).
bool sameSign = (xInt ^ yInt) > -1;
// If the inputs have the same sign, the result should be positive. Otherwise, it should be negative.
unchecked {
result = wrap(sameSign ? int256(resultAbs) : -int256(resultAbs));
}
}
/// @notice Raises x to the power of y using the following formula:
///
/// $$
/// x^y = 2^{log_2{x} * y}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {exp2}, {log2}, and {mul}.
/// - Returns `UNIT` for 0^0.
///
/// Requirements:
/// - Refer to the requirements in {exp2}, {log2}, and {mul}.
///
/// @param x The base as an SD59x18 number.
/// @param y Exponent to raise x to, as an SD59x18 number
/// @return result x raised to power y, as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function pow(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
int256 yInt = y.unwrap();
// If both x and y are zero, the result is `UNIT`. If just x is zero, the result is always zero.
if (xInt == 0) {
return yInt == 0 ? UNIT : ZERO;
}
// If x is `UNIT`, the result is always `UNIT`.
else if (xInt == uUNIT) {
return UNIT;
}
// If y is zero, the result is always `UNIT`.
if (yInt == 0) {
return UNIT;
}
// If y is `UNIT`, the result is always x.
else if (yInt == uUNIT) {
return x;
}
// Calculate the result using the formula.
result = exp2(mul(log2(x), y));
}
/// @notice Raises x (an SD59x18 number) to the power y (an unsigned basic integer) using the well-known
/// algorithm "exponentiation by squaring".
///
/// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv18}.
/// - Returns `UNIT` for 0^0.
///
/// Requirements:
/// - Refer to the requirements in {abs} and {Common.mulDiv18}.
/// - The result must fit in SD59x18.
///
/// @param x The base as an SD59x18 number.
/// @param y The exponent as a uint256.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function powu(SD59x18 x, uint256 y) pure returns (SD59x18 result) {
uint256 xAbs = uint256(abs(x).unwrap());
// Calculate the first iteration of the loop in advance.
uint256 resultAbs = y & 1 > 0 ? xAbs : uint256(uUNIT);
// Equivalent to `for(y /= 2; y > 0; y /= 2)`.
uint256 yAux = y;
for (yAux >>= 1; yAux > 0; yAux >>= 1) {
xAbs = Common.mulDiv18(xAbs, xAbs);
// Equivalent to `y % 2 == 1`.
if (yAux & 1 > 0) {
resultAbs = Common.mulDiv18(resultAbs, xAbs);
}
}
// The result must fit in SD59x18.
if (resultAbs > uint256(uMAX_SD59x18)) {
revert Errors.PRBMath_SD59x18_Powu_Overflow(x, y);
}
unchecked {
// Is the base negative and the exponent odd? If yes, the result should be negative.
int256 resultInt = int256(resultAbs);
bool isNegative = x.unwrap() < 0 && y & 1 == 1;
if (isNegative) {
resultInt = -resultInt;
}
result = wrap(resultInt);
}
}
/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - Only the positive root is returned.
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x cannot be negative, since complex numbers are not supported.
/// - x must be less than `MAX_SD59x18 / UNIT`.
///
/// @param x The SD59x18 number for which to calculate the square root.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function sqrt(SD59x18 x) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
if (xInt < 0) {
revert Errors.PRBMath_SD59x18_Sqrt_NegativeInput(x);
}
if (xInt > uMAX_SD59x18 / uUNIT) {
revert Errors.PRBMath_SD59x18_Sqrt_Overflow(x);
}
unchecked {
// Multiply x by `UNIT` to account for the factor of `UNIT` picked up when multiplying two SD59x18 numbers.
// In this case, the two numbers are both the square root.
uint256 resultUint = Common.sqrt(uint256(xInt * uUNIT));
result = wrap(int256(resultUint));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { wrap } from "./Casting.sol";
import { SD59x18 } from "./ValueType.sol";
/// @notice Implements the checked addition operation (+) in the SD59x18 type.
function add(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
return wrap(x.unwrap() + y.unwrap());
}
/// @notice Implements the AND (&) bitwise operation in the SD59x18 type.
function and(SD59x18 x, int256 bits) pure returns (SD59x18 result) {
return wrap(x.unwrap() & bits);
}
/// @notice Implements the AND (&) bitwise operation in the SD59x18 type.
function and2(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
return wrap(x.unwrap() & y.unwrap());
}
/// @notice Implements the equal (=) operation in the SD59x18 type.
function eq(SD59x18 x, SD59x18 y) pure returns (bool result) {
result = x.unwrap() == y.unwrap();
}
/// @notice Implements the greater than operation (>) in the SD59x18 type.
function gt(SD59x18 x, SD59x18 y) pure returns (bool result) {
result = x.unwrap() > y.unwrap();
}
/// @notice Implements the greater than or equal to operation (>=) in the SD59x18 type.
function gte(SD59x18 x, SD59x18 y) pure returns (bool result) {
result = x.unwrap() >= y.unwrap();
}
/// @notice Implements a zero comparison check function in the SD59x18 type.
function isZero(SD59x18 x) pure returns (bool result) {
result = x.unwrap() == 0;
}
/// @notice Implements the left shift operation (<<) in the SD59x18 type.
function lshift(SD59x18 x, uint256 bits) pure returns (SD59x18 result) {
result = wrap(x.unwrap() << bits);
}
/// @notice Implements the lower than operation (<) in the SD59x18 type.
function lt(SD59x18 x, SD59x18 y) pure returns (bool result) {
result = x.unwrap() < y.unwrap();
}
/// @notice Implements the lower than or equal to operation (<=) in the SD59x18 type.
function lte(SD59x18 x, SD59x18 y) pure returns (bool result) {
result = x.unwrap() <= y.unwrap();
}
/// @notice Implements the unchecked modulo operation (%) in the SD59x18 type.
function mod(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
result = wrap(x.unwrap() % y.unwrap());
}
/// @notice Implements the not equal operation (!=) in the SD59x18 type.
function neq(SD59x18 x, SD59x18 y) pure returns (bool result) {
result = x.unwrap() != y.unwrap();
}
/// @notice Implements the NOT (~) bitwise operation in the SD59x18 type.
function not(SD59x18 x) pure returns (SD59x18 result) {
result = wrap(~x.unwrap());
}
/// @notice Implements the OR (|) bitwise operation in the SD59x18 type.
function or(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
result = wrap(x.unwrap() | y.unwrap());
}
/// @notice Implements the right shift operation (>>) in the SD59x18 type.
function rshift(SD59x18 x, uint256 bits) pure returns (SD59x18 result) {
result = wrap(x.unwrap() >> bits);
}
/// @notice Implements the checked subtraction operation (-) in the SD59x18 type.
function sub(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
result = wrap(x.unwrap() - y.unwrap());
}
/// @notice Implements the checked unary minus operation (-) in the SD59x18 type.
function unary(SD59x18 x) pure returns (SD59x18 result) {
result = wrap(-x.unwrap());
}
/// @notice Implements the unchecked addition operation (+) in the SD59x18 type.
function uncheckedAdd(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
unchecked {
result = wrap(x.unwrap() + y.unwrap());
}
}
/// @notice Implements the unchecked subtraction operation (-) in the SD59x18 type.
function uncheckedSub(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
unchecked {
result = wrap(x.unwrap() - y.unwrap());
}
}
/// @notice Implements the unchecked unary minus operation (-) in the SD59x18 type.
function uncheckedUnary(SD59x18 x) pure returns (SD59x18 result) {
unchecked {
result = wrap(-x.unwrap());
}
}
/// @notice Implements the XOR (^) bitwise operation in the SD59x18 type.
function xor(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
result = wrap(x.unwrap() ^ y.unwrap());
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "./Errors.sol" as CastingErrors;
import { MAX_UINT128, MAX_UINT40 } from "../Common.sol";
import { uMAX_SD1x18, uMIN_SD1x18 } from "../sd1x18/Constants.sol";
import { SD1x18 } from "../sd1x18/ValueType.sol";
import { uMAX_UD2x18 } from "../ud2x18/Constants.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";
import { SD59x18 } from "./ValueType.sol";
/// @notice Casts an SD59x18 number into int256.
/// @dev This is basically a functional alias for {unwrap}.
function intoInt256(SD59x18 x) pure returns (int256 result) {
result = SD59x18.unwrap(x);
}
/// @notice Casts an SD59x18 number into SD1x18.
/// @dev Requirements:
/// - x must be greater than or equal to `uMIN_SD1x18`.
/// - x must be less than or equal to `uMAX_SD1x18`.
function intoSD1x18(SD59x18 x) pure returns (SD1x18 result) {
int256 xInt = SD59x18.unwrap(x);
if (xInt < uMIN_SD1x18) {
revert CastingErrors.PRBMath_SD59x18_IntoSD1x18_Underflow(x);
}
if (xInt > uMAX_SD1x18) {
revert CastingErrors.PRBMath_SD59x18_IntoSD1x18_Overflow(x);
}
result = SD1x18.wrap(int64(xInt));
}
/// @notice Casts an SD59x18 number into UD2x18.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `uMAX_UD2x18`.
function intoUD2x18(SD59x18 x) pure returns (UD2x18 result) {
int256 xInt = SD59x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD59x18_IntoUD2x18_Underflow(x);
}
if (xInt > int256(uint256(uMAX_UD2x18))) {
revert CastingErrors.PRBMath_SD59x18_IntoUD2x18_Overflow(x);
}
result = UD2x18.wrap(uint64(uint256(xInt)));
}
/// @notice Casts an SD59x18 number into UD60x18.
/// @dev Requirements:
/// - x must be positive.
function intoUD60x18(SD59x18 x) pure returns (UD60x18 result) {
int256 xInt = SD59x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD59x18_IntoUD60x18_Underflow(x);
}
result = UD60x18.wrap(uint256(xInt));
}
/// @notice Casts an SD59x18 number into uint256.
/// @dev Requirements:
/// - x must be positive.
function intoUint256(SD59x18 x) pure returns (uint256 result) {
int256 xInt = SD59x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD59x18_IntoUint256_Underflow(x);
}
result = uint256(xInt);
}
/// @notice Casts an SD59x18 number into uint128.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `uMAX_UINT128`.
function intoUint128(SD59x18 x) pure returns (uint128 result) {
int256 xInt = SD59x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD59x18_IntoUint128_Underflow(x);
}
if (xInt > int256(uint256(MAX_UINT128))) {
revert CastingErrors.PRBMath_SD59x18_IntoUint128_Overflow(x);
}
result = uint128(uint256(xInt));
}
/// @notice Casts an SD59x18 number into uint40.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(SD59x18 x) pure returns (uint40 result) {
int256 xInt = SD59x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD59x18_IntoUint40_Underflow(x);
}
if (xInt > int256(uint256(MAX_UINT40))) {
revert CastingErrors.PRBMath_SD59x18_IntoUint40_Overflow(x);
}
result = uint40(uint256(xInt));
}
/// @notice Alias for {wrap}.
function sd(int256 x) pure returns (SD59x18 result) {
result = SD59x18.wrap(x);
}
/// @notice Alias for {wrap}.
function sd59x18(int256 x) pure returns (SD59x18 result) {
result = SD59x18.wrap(x);
}
/// @notice Unwraps an SD59x18 number into int256.
function unwrap(SD59x18 x) pure returns (int256 result) {
result = SD59x18.unwrap(x);
}
/// @notice Wraps an int256 number into SD59x18.
function wrap(int256 x) pure returns (SD59x18 result) {
result = SD59x18.wrap(x);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "../Common.sol" as Common;
import "./Errors.sol" as CastingErrors;
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";
import { SD1x18 } from "./ValueType.sol";
/// @notice Casts an SD1x18 number into SD59x18.
/// @dev There is no overflow check because the domain of SD1x18 is a subset of SD59x18.
function intoSD59x18(SD1x18 x) pure returns (SD59x18 result) {
result = SD59x18.wrap(int256(SD1x18.unwrap(x)));
}
/// @notice Casts an SD1x18 number into UD2x18.
/// - x must be positive.
function intoUD2x18(SD1x18 x) pure returns (UD2x18 result) {
int64 xInt = SD1x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD1x18_ToUD2x18_Underflow(x);
}
result = UD2x18.wrap(uint64(xInt));
}
/// @notice Casts an SD1x18 number into UD60x18.
/// @dev Requirements:
/// - x must be positive.
function intoUD60x18(SD1x18 x) pure returns (UD60x18 result) {
int64 xInt = SD1x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD1x18_ToUD60x18_Underflow(x);
}
result = UD60x18.wrap(uint64(xInt));
}
/// @notice Casts an SD1x18 number into uint256.
/// @dev Requirements:
/// - x must be positive.
function intoUint256(SD1x18 x) pure returns (uint256 result) {
int64 xInt = SD1x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD1x18_ToUint256_Underflow(x);
}
result = uint256(uint64(xInt));
}
/// @notice Casts an SD1x18 number into uint128.
/// @dev Requirements:
/// - x must be positive.
function intoUint128(SD1x18 x) pure returns (uint128 result) {
int64 xInt = SD1x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD1x18_ToUint128_Underflow(x);
}
result = uint128(uint64(xInt));
}
/// @notice Casts an SD1x18 number into uint40.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(SD1x18 x) pure returns (uint40 result) {
int64 xInt = SD1x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD1x18_ToUint40_Underflow(x);
}
if (xInt > int64(uint64(Common.MAX_UINT40))) {
revert CastingErrors.PRBMath_SD1x18_ToUint40_Overflow(x);
}
result = uint40(uint64(xInt));
}
/// @notice Alias for {wrap}.
function sd1x18(int64 x) pure returns (SD1x18 result) {
result = SD1x18.wrap(x);
}
/// @notice Unwraps an SD1x18 number into int64.
function unwrap(SD1x18 x) pure returns (int64 result) {
result = SD1x18.unwrap(x);
}
/// @notice Wraps an int64 number into SD1x18.
function wrap(int64 x) pure returns (SD1x18 result) {
result = SD1x18.wrap(x);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { UD2x18 } from "./ValueType.sol";
/// @notice Thrown when trying to cast a UD2x18 number that doesn't fit in SD1x18.
error PRBMath_UD2x18_IntoSD1x18_Overflow(UD2x18 x);
/// @notice Thrown when trying to cast a UD2x18 number that doesn't fit in uint40.
error PRBMath_UD2x18_IntoUint40_Overflow(UD2x18 x);// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { SD59x18 } from "./ValueType.sol";
/// @notice Thrown when taking the absolute value of `MIN_SD59x18`.
error PRBMath_SD59x18_Abs_MinSD59x18();
/// @notice Thrown when ceiling a number overflows SD59x18.
error PRBMath_SD59x18_Ceil_Overflow(SD59x18 x);
/// @notice Thrown when converting a basic integer to the fixed-point format overflows SD59x18.
error PRBMath_SD59x18_Convert_Overflow(int256 x);
/// @notice Thrown when converting a basic integer to the fixed-point format underflows SD59x18.
error PRBMath_SD59x18_Convert_Underflow(int256 x);
/// @notice Thrown when dividing two numbers and one of them is `MIN_SD59x18`.
error PRBMath_SD59x18_Div_InputTooSmall();
/// @notice Thrown when dividing two numbers and one of the intermediary unsigned results overflows SD59x18.
error PRBMath_SD59x18_Div_Overflow(SD59x18 x, SD59x18 y);
/// @notice Thrown when taking the natural exponent of a base greater than 133_084258667509499441.
error PRBMath_SD59x18_Exp_InputTooBig(SD59x18 x);
/// @notice Thrown when taking the binary exponent of a base greater than 192e18.
error PRBMath_SD59x18_Exp2_InputTooBig(SD59x18 x);
/// @notice Thrown when flooring a number underflows SD59x18.
error PRBMath_SD59x18_Floor_Underflow(SD59x18 x);
/// @notice Thrown when taking the geometric mean of two numbers and their product is negative.
error PRBMath_SD59x18_Gm_NegativeProduct(SD59x18 x, SD59x18 y);
/// @notice Thrown when taking the geometric mean of two numbers and multiplying them overflows SD59x18.
error PRBMath_SD59x18_Gm_Overflow(SD59x18 x, SD59x18 y);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18.
error PRBMath_SD59x18_IntoSD1x18_Overflow(SD59x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18.
error PRBMath_SD59x18_IntoSD1x18_Underflow(SD59x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18.
error PRBMath_SD59x18_IntoUD2x18_Overflow(SD59x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18.
error PRBMath_SD59x18_IntoUD2x18_Underflow(SD59x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD60x18.
error PRBMath_SD59x18_IntoUD60x18_Underflow(SD59x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128.
error PRBMath_SD59x18_IntoUint128_Overflow(SD59x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128.
error PRBMath_SD59x18_IntoUint128_Underflow(SD59x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint256.
error PRBMath_SD59x18_IntoUint256_Underflow(SD59x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint40.
error PRBMath_SD59x18_IntoUint40_Overflow(SD59x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint40.
error PRBMath_SD59x18_IntoUint40_Underflow(SD59x18 x);
/// @notice Thrown when taking the logarithm of a number less than or equal to zero.
error PRBMath_SD59x18_Log_InputTooSmall(SD59x18 x);
/// @notice Thrown when multiplying two numbers and one of the inputs is `MIN_SD59x18`.
error PRBMath_SD59x18_Mul_InputTooSmall();
/// @notice Thrown when multiplying two numbers and the intermediary absolute result overflows SD59x18.
error PRBMath_SD59x18_Mul_Overflow(SD59x18 x, SD59x18 y);
/// @notice Thrown when raising a number to a power and the intermediary absolute result overflows SD59x18.
error PRBMath_SD59x18_Powu_Overflow(SD59x18 x, uint256 y);
/// @notice Thrown when taking the square root of a negative number.
error PRBMath_SD59x18_Sqrt_NegativeInput(SD59x18 x);
/// @notice Thrown when the calculating the square root overflows SD59x18.
error PRBMath_SD59x18_Sqrt_Overflow(SD59x18 x);// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { SD1x18 } from "./ValueType.sol";
/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in UD2x18.
error PRBMath_SD1x18_ToUD2x18_Underflow(SD1x18 x);
/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in UD60x18.
error PRBMath_SD1x18_ToUD60x18_Underflow(SD1x18 x);
/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint128.
error PRBMath_SD1x18_ToUint128_Underflow(SD1x18 x);
/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint256.
error PRBMath_SD1x18_ToUint256_Underflow(SD1x18 x);
/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint40.
error PRBMath_SD1x18_ToUint40_Overflow(SD1x18 x);
/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint40.
error PRBMath_SD1x18_ToUint40_Underflow(SD1x18 x);{
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": [
"forge-std/=lib/forge-std/src/",
"@prb/math/=lib/prb-math/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/"
]
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"echoAdmin","type":"address"},{"internalType":"address","name":"currentImplementation","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"maxCampaignTime","type":"uint256"}],"name":"CHECK_MAX_CAMPAIGN_DURATION","type":"error"},{"inputs":[],"name":"END_TIME_LOWER_THAN_START_TIME","type":"error"},{"inputs":[],"name":"MAX_PER_POST_BIGGER_THAN_RESERVED_AMOUNT","type":"error"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"clonedCampaign","type":"address"}],"name":"NAME_EXISTS","type":"error"},{"inputs":[],"name":"NAME_LENGTH_LONGER_THAN_64_BYTES","type":"error"},{"inputs":[],"name":"NAME_LENGTH_LOWER_THAN_8_BYTES","type":"error"},{"inputs":[],"name":"NO_KPIS","type":"error"},{"inputs":[],"name":"NO_QUALIFICATION","type":"error"},{"inputs":[],"name":"NULL_CID","type":"error"},{"inputs":[],"name":"ONLY_ALLOWED_SOCIAL_KPIS","type":"error"},{"inputs":[],"name":"ONLY_PROTOCOL_ADMIN","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"ONLY_WHITELISTED_TOKEN","type":"error"},{"inputs":[{"internalType":"string","name":"qaActionKind","type":"string"}],"name":"QA_METHOD_OR_ORACLE_NOT_ALLOWED","type":"error"},{"inputs":[{"internalType":"string","name":"kpiActionKind","type":"string"}],"name":"SOCIAL_KPI_RATIO_IS_ZERO","type":"error"},{"inputs":[],"name":"START_TIME_IN_PAST","type":"error"},{"inputs":[{"internalType":"uint256","name":"sumOfPct","type":"uint256"}],"name":"SUM_OF_QUALIFICATION_PARAMS_MUST_BE_100","type":"error"},{"inputs":[{"internalType":"uint256","name":"sumOfPct","type":"uint256"}],"name":"SUM_OF_SOCIAL_KPI_PARAMS_MUST_BE_100","type":"error"},{"inputs":[],"name":"ZERO_ADDRESS_PROVIDED","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"clonedCampaign","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"implementation","type":"address"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"uint256","name":"applicationFee","type":"uint256"},{"indexed":false,"internalType":"string","name":"EchoMarketData","type":"string"},{"indexed":false,"internalType":"uint256","name":"totalClonedCampaigns","type":"uint256"},{"components":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"address","name":"refundAddress","type":"address"},{"internalType":"string","name":"ipfsCID","type":"string"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint128","name":"maxPerPost","type":"uint128"},{"internalType":"uint128","name":"reservedAmount","type":"uint128"}],"internalType":"struct BudgetInfo","name":"budgetInfo","type":"tuple"},{"components":[{"internalType":"string","name":"social","type":"string"},{"components":[{"internalType":"uint8","name":"pct","type":"uint8"},{"internalType":"uint248","name":"min","type":"uint248"},{"internalType":"uint256","name":"ratio","type":"uint256"},{"internalType":"string","name":"method","type":"string"},{"internalType":"string","name":"extra","type":"string"}],"internalType":"struct KPI[]","name":"kpis","type":"tuple[]"}],"internalType":"struct SocialKPIs","name":"socialKPIs","type":"tuple"},{"components":[{"internalType":"enum QA_METHOD","name":"method","type":"uint8"},{"internalType":"uint88","name":"pct","type":"uint88"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"string","name":"kind","type":"string"}],"internalType":"struct QualificationData[]","name":"qaData","type":"tuple[]"}],"indexed":false,"internalType":"struct InitCampaign","name":"initCampaign","type":"tuple"}],"name":"CampaignCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lastImplementation","type":"address"},{"indexed":true,"internalType":"address","name":"newImplementation","type":"address"}],"name":"ImplementationChanged","type":"event"},{"inputs":[],"name":"ECHO_ADMIN","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ECHO_CONTENTS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allCampaigns","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"campaignNameHashToName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"campaignNameToInitData","outputs":[{"components":[{"internalType":"uint64","name":"initTime","type":"uint64"},{"internalType":"address","name":"clonedCampaign","type":"address"},{"internalType":"address","name":"implementation","type":"address"}],"internalType":"struct CampaignInitData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"changeImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"string","name":"name","type":"string"},{"components":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"address","name":"refundAddress","type":"address"},{"internalType":"string","name":"ipfsCID","type":"string"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint128","name":"maxPerPost","type":"uint128"},{"internalType":"uint128","name":"reservedAmount","type":"uint128"}],"internalType":"struct BudgetInfo","name":"budgetInfo","type":"tuple"},{"components":[{"internalType":"string","name":"social","type":"string"},{"components":[{"internalType":"uint8","name":"pct","type":"uint8"},{"internalType":"uint248","name":"min","type":"uint248"},{"internalType":"uint256","name":"ratio","type":"uint256"},{"internalType":"string","name":"method","type":"string"},{"internalType":"string","name":"extra","type":"string"}],"internalType":"struct KPI[]","name":"kpis","type":"tuple[]"}],"internalType":"struct SocialKPIs","name":"socialKPIs","type":"tuple"},{"components":[{"internalType":"enum QA_METHOD","name":"method","type":"uint8"},{"internalType":"uint88","name":"pct","type":"uint88"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"string","name":"kind","type":"string"}],"internalType":"struct QualificationData[]","name":"qaData","type":"tuple[]"}],"internalType":"struct InitCampaign","name":"initCampaign","type":"tuple"}],"name":"createCampaign","outputs":[{"internalType":"address","name":"clonedCampaign","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"page","type":"uint256"}],"name":"paginatedCampaigns","outputs":[{"internalType":"uint256","name":"currentPage","type":"uint256"},{"internalType":"uint256","name":"totalPages","type":"uint256"},{"internalType":"address[]","name":"pagedArray","type":"address[]"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60c060405234801561000f575f5ffd5b506040516181ed3803806181ed83398101604081905261002e916101b9565b61003782610167565b61004081610167565b6001600160a01b038216608052604051829061005b90610191565b6001600160a01b039091168152602001604051809103905ff080158015610084573d5f5f3e3d5ffd5b506001600160a01b0390811660a0525f80546001600160a01b0319169183169190911781555b601881101561015f5760405163a509f4d160e01b815260048101829052600190738f1e629536c74e8d8f58cfcbb44ad9cdf66a24339063a509f4d190602401602060405180830381865afa158015610104573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061012891906101ea565b8154600180820184555f93845260209093200180546001600160a01b0319166001600160a01b0392909216919091179055016100aa565b50505061020a565b6001600160a01b03811661018e5760405163140efc4d60e21b815260040160405180910390fd5b50565b61564e80612b9f83390190565b80516001600160a01b03811681146101b4575f5ffd5b919050565b5f5f604083850312156101ca575f5ffd5b6101d38361019e565b91506101e16020840161019e565b90509250929050565b5f602082840312156101fa575f5ffd5b6102038261019e565b9392505050565b60805160a05161292e6102715f395f61016101525f818161019b0152818161031b015281816103c00152818161045201528181610728015281816109f501528181610b9001528181610c3c01528181610d1e01528181611766015261180f015261292e5ff3fe608060405234801561000f575f5ffd5b5060043610610090575f3560e01c80635c60da1b116100635780635c60da1b1461014a5780639b5e6a8b1461015c578063a509f4d114610183578063cfce1a9914610196578063fe4d5536146101bd575f5ffd5b80630897d6f0146100945780630caba05e146100c457806317a68dd8146100e65780632c960900146100fb575b5f5ffd5b6100a76100a2366004611ee4565b6101dd565b6040516001600160a01b0390911681526020015b60405180910390f35b6100d76100d236600461201c565b611162565b6040516100bb93929190612033565b6100f96100f4366004612090565b611484565b005b61010e6101093660046120b2565b61151f565b6040805182516001600160401b031681526020808401516001600160a01b039081169183019190915292820151909216908201526060016100bb565b5f546100a7906001600160a01b031681565b6100a77f000000000000000000000000000000000000000000000000000000000000000081565b6100a761019136600461201c565b61165e565b6100a77f000000000000000000000000000000000000000000000000000000000000000081565b6101d06101cb36600461201c565b611686565b6040516100bb919061211e565b5f6101e78561171d565b6080820151602001516001600160801b0316156102105761020b826040015161171d565b610217565b5f60408301525b60088310156102395760405163a9cac3f760e01b815260040160405180910390fd5b604083111561025b57604051638af6981560e01b815260040160405180910390fd5b8160600151515f036102805760405163d57e933160e01b815260040160405180910390fd5b5f61028b858561151f565b602001516001600160a01b0316146102d25783836102a9868661151f565b602001516040516307bbbbbf60e21b81526004016102c993929190612158565b60405180910390fd5b81514211156102f457604051634ef659b360e11b815260040160405180910390fd5b815160208301511161031957604051634d9b1b8b60e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663bec4d54e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610375573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103999190612183565b68ffffffffffffffffff161580159061044b5750815160208301516103be91906121bf565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663bec4d54e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561041a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061043e9190612183565b68ffffffffffffffffff16105b156104f7577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663bec4d54e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104ac573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104d09190612183565b604051637d1cd12360e11b815268ffffffffffffffffff90911660048201526024016102c9565b60808201515161050690611747565b6080820151602001516001600160801b03161580159061054757508160800151602001516001600160801b03168260800151604001516001600160801b0316105b15610565576040516308cd810560e01b815260040160405180910390fd5b8160c00151515f0361058a5760405163656f350160e01b815260040160405180910390fd5b6040805160018082528183019092525f918291606091839190602080830190803683370190505090505b8560c001515183101561087a575f8660c0015184815181106105d8576105d86121e6565b60200260200101515f015160028111156105f4576105f46121d2565b036106205760405180604001604052806007815260200166028a096a0a49d160cd1b81525091506106a1565b60018660c001518481518110610638576106386121e6565b60200260200101515f01516002811115610654576106546121d2565b0361067f5760405180604001604052806006815260200165028a096a19d160d51b81525091506106a1565b60405180604001604052806006815260200165028a096ab1d160d51b81525091505b818660c0015184815181106106b8576106b86121e6565b6020026020010151606001516040516020016106d5929190612211565b60408051601f19818403018152908290526106f291602001612225565b60405160208183030381529060405280519060200120815f8151811061071a5761071a6121e6565b6020026020010181815250507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663e6e2be168760c00151858151811061076b5761076b6121e6565b602002602001015160400151836040518363ffffffff1660e01b8152600401610795929190612230565b602060405180830381865afa1580156107b0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107d49190612285565b61083357818660c0015184815181106107ef576107ef6121e6565b60200260200101516060015160405160200161080c929190612211565b60408051601f1981840301815290829052633ddcaa5960e11b82526102c99160040161211e565b8560c001518381518110610849576108496121e6565b6020026020010151602001516affffffffffffffffffffff168461086d91906122a4565b93506001909201916105b4565b8360641461089e5760405163ecf0160d60e01b8152600481018590526024016102c9565b5f92505f93508560a0015160200151515f036108cd576040516379a7f53760e01b815260040160405180910390fd5b5f8660a0015160200151516001600160401b038111156108ef576108ef611a54565b604051908082528060200260200182016040528015610918578160200160208202803683370190505b5090505b8660a001516020015151841015610b55578660a00151602001518481518110610947576109476121e6565b60200260200101515f015160ff168561096091906122a4565b94508660a001515f01518760a00151602001518581518110610984576109846121e6565b6020026020010151606001516040516020016109a19291906122b7565b60408051601f19818403018152908290526109be91602001612225565b604051602081830303815290604052805190602001208185815181106109e6576109e66121e6565b6020026020010181815250505f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b6001e245f848881518110610a3557610a356121e6565b60200260200101516040518363ffffffff1660e01b8152600401610a6e9291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa158015610a89573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610aad91906122e0565b505060ff16915050805f03610b1f5760a08801518051602090910151805187908110610adb57610adb6121e6565b602002602001015160600151604051602001610af89291906122b7565b60408051601f198184030181529082905263ba7fb2ed60e01b82526102c99160040161211e565b808860a00151602001518681518110610b3a57610b3a6121e6565b6020908102919091010151604001525060019093019261091c565b84606414610b7957604051630d738e1160e31b8152600481018690526024016102c9565b6040516373715f0b60e11b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063e6e2be1690610bc7905f908590600401612230565b602060405180830381865afa158015610be2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c069190612285565b610c2357604051630361692160e51b815260040160405180910390fd5b610d155f5f9054906101000a90046001600160a01b03167f0000000000000000000000000000000000000000000000000000000000000000308d8b604001518e8e604051610c7292919061233f565b60405180910390205f1c8d5f01518e60200151604051602001610ce99796959493929190606097881b6bffffffffffffffffffffffff19908116825296881b8716601482015294871b861660288601529290951b909316603c83015260508201929092526070810192909252609082015260b00190565b6040516020818303038152906040528b8b604051610d0892919061233f565b60405180910390206117f7565b95505f620186a07f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638ddc321f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d78573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d9c919061234e565b62ffffff16896080015160400151610db49190612370565b610dbe91906123ad565b9050808860800151604001818151610dd691906123da565b6001600160801b03169052506060880151608089015160a08a015160c08b0151604051631d00132960e31b81526001600160a01b038c169463e800994894610e24949193909260040161259c565b5f604051808303815f87803b158015610e3b575f5ffd5b505af1158015610e4d573d5f5f3e3d5ffd5b505050506080880151516001600160a01b03166323b872dd33610e6e61180c565b6040516001600160e01b031960e085901b1681526001600160a01b039283166004820152911660248201526001600160801b03841660448201526064016020604051808303815f875af1158015610ec7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610eeb9190612285565b506080880151805160409182015191516323b872dd60e01b81523360048201526001600160a01b038a811660248301526001600160801b0390931660448201529116906323b872dd906064016020604051808303815f875af1158015610f53573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f779190612285565b50604080516060810182526001600160401b03421681526001600160a01b03808a1660208301525f5416818301529051600390610fb7908d908d9061233f565b908152604080516020928190038301812084518154948601516001600160401b039091166001600160e01b031990951694909417600160401b6001600160a01b03958616021781559390910151600190930180546001600160a01b031916939092169290921790558a908a906002905f90611035908590859061233f565b604051809103902081526020019081526020015f209182611057929190612670565b506001805480820182555f9182527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b0319166001600160a01b038a81169182179092559154604080516304cbb47f60e01b8152905191831693928f1692917f95f1e03f5a5037130736d042aaa0cf4d85d403655b81b55612c57126f636fa92918f918f9185916304cbb47f916004808201926020929091908290030181865afa158015611110573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111349190612729565b6001805490508f60405161114c959493929190612740565b60405180910390a4505050505050949350505050565b6001545f9081906060901561147d57600154600b111561125d576001546001600160401b0381111561119657611196611a54565b6040519080825280602002602001820160405280156111bf578160200160208202803683370190505b5090505f5b6001805482906111d59083906121bf565b6111df91906121bf565b815481106111ef576111ef6121e6565b905f5260205f20015f9054906101000a90046001600160a01b031682828151811061121c5761121c6121e6565b6001600160a01b03909216602092830291909101909101526001805461124291906121bf565b8114611250576001016111c4565b600180935093505061147d565b835f0361126957600193505b60015461127890600a90612829565b91505f61128683600a61283c565b60015461129391906121bf565b6001549091506112a484600a61283c565b10156112b857826112b481612853565b9350505b828511156112c4578294505b8493505f5f866001036112f357600180546112df91906121bf565b91506112ec600a836121bf565b90506113b0565b84870361131d5782156113105761130b6001846121bf565b611316565b60099150815b91506113b0565b825f0361132a575f611335565b6113356001846121bf565b61133f88876121bf565b61134a90600a61283c565b61135491906122a4565b61135e90836122a4565b9150825f0361136d575f611378565b6113786001846121bf565b600161138489886121bf565b61138e91906121bf565b61139990600a61283c565b6113a391906122a4565b6113ad90826122a4565b90505b806113bc8360016122a4565b6113c691906121bf565b6001600160401b038111156113dd576113dd611a54565b604051908082528060200260200182016040528015611406578160200160208202803683370190505b5093505f5b6001838154811061141e5761141e6121e6565b905f5260205f20015f9054906101000a90046001600160a01b031685828151811061144b5761144b6121e6565b6001600160a01b0390921660209283029190910190910152828214611478575f199092019160010161140b565b505050505b9193909250565b61148c61180c565b6001600160a01b0316336001600160a01b0316146114bd576040516315cead3960e31b815260040160405180910390fd5b6114c68161171d565b5f80546040516001600160a01b03808516939216917fcfbf4028add9318bbf716f08c348595afb063b0e9feed1f86d33681a4b3ed4d391a35f80546001600160a01b0319166001600160a01b0392909216919091179055565b604080516060810182525f80825260208201819052918101919091525f6001600160a01b03166003848460405161155792919061233f565b908152604051908190036020019020546001600160a01b03600160401b90910416036115fb57604051622c960960e81b8152738f1e629536c74e8d8f58cfcbb44ad9cdf66a243390632c960900906115b5908690869060040161286b565b606060405180830381865afa1580156115d0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115f4919061287e565b9050611658565b6003838360405161160d92919061233f565b90815260408051918290036020908101832060608401835280546001600160401b03811685526001600160a01b03600160401b90910481169285019290925260010154169082015290505b92915050565b6001818154811061166d575f80fd5b5f918252602090912001546001600160a01b0316905081565b60026020525f90815260409020805461169e906125ec565b80601f01602080910402602001604051908101604052809291908181526020018280546116ca906125ec565b80156117155780601f106116ec57610100808354040283529160200191611715565b820191905f5260205f20905b8154815290600101906020018083116116f857829003601f168201915b505050505081565b6001600160a01b0381166117445760405163140efc4d60e21b815260040160405180910390fd5b50565b6040516338b317cf60e21b81526001600160a01b0382811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063e2cc5f3c90602401602060405180830381865afa1580156117ab573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117cf9190612285565b6117445760405163c1c1013b60e01b81526001600160a01b03821660048201526024016102c9565b5f6118048484845f611892565b949350505050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa158015611869573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061188d91906128dd565b905090565b8251604051613d6160f01b8152603a820160f090811b6002838101919091526680600b3d3981f360c81b600484015264363d3d376160d81b600b8401528301901b6010820181905268603836393d3d3d366160b81b6012830152601b82015262013d7360e81b601d820152606086901b6020808301919091526e5af43d82803e903d91603657fd5bf360881b60348301525f929160458301918390604383019089015b602083106119545780518252601f199092019160209182019101611935565b5f6001846020036101000a0319905080825116835283830192508660f01b83526f67363d3d37363d34f03d5260086018f35f52896010805ff58061199f5763ebfef1885f526004601cfd5b8060145261d6945f5260016034536001600160a01b036017601e201698505f5f88888d855af115893b1517156119dc57638f86d2f15f526004601cfd5b5050505050505050949350505050565b6001600160a01b0381168114611744575f5ffd5b8035611a0b816119ec565b919050565b5f5f83601f840112611a20575f5ffd5b5081356001600160401b03811115611a36575f5ffd5b602083019150836020828501011115611a4d575f5ffd5b9250929050565b634e487b7160e01b5f52604160045260245ffd5b604051606081016001600160401b0381118282101715611a8a57611a8a611a54565b60405290565b604080519081016001600160401b0381118282101715611a8a57611a8a611a54565b60405160a081016001600160401b0381118282101715611a8a57611a8a611a54565b604051608081016001600160401b0381118282101715611a8a57611a8a611a54565b60405160e081016001600160401b0381118282101715611a8a57611a8a611a54565b604051601f8201601f191681016001600160401b0381118282101715611b4057611b40611a54565b604052919050565b5f82601f830112611b57575f5ffd5b81356001600160401b03811115611b7057611b70611a54565b611b83601f8201601f1916602001611b18565b818152846020838601011115611b97575f5ffd5b816020850160208301375f918101602001919091529392505050565b80356001600160801b0381168114611a0b575f5ffd5b5f60608284031215611bd9575f5ffd5b611be1611a68565b90508135611bee816119ec565b8152611bfc60208301611bb3565b6020820152611c0d60408301611bb3565b604082015292915050565b5f6001600160401b03821115611c3057611c30611a54565b5060051b60200190565b60ff81168114611744575f5ffd5b6001600160f81b0381168114611744575f5ffd5b5f60408284031215611c6c575f5ffd5b611c74611a90565b905081356001600160401b03811115611c8b575f5ffd5b611c9784828501611b48565b82525060208201356001600160401b03811115611cb2575f5ffd5b8201601f81018413611cc2575f5ffd5b8035611cd5611cd082611c18565b611b18565b8082825260208201915060208360051b850101925086831115611cf6575f5ffd5b602084015b83811015611dcb5780356001600160401b03811115611d18575f5ffd5b850160a0818a03601f19011215611d2d575f5ffd5b611d35611ab2565b6020820135611d4381611c3a565b81526040820135611d5381611c48565b60208201526060820135604082015260808201356001600160401b03811115611d7a575f5ffd5b611d898b602083860101611b48565b60608301525060a08201356001600160401b03811115611da7575f5ffd5b611db68b602083860101611b48565b60808301525084525060209283019201611cfb565b5060208501525091949350505050565b5f82601f830112611dea575f5ffd5b8135611df8611cd082611c18565b8082825260208201915060208360051b860101925085831115611e19575f5ffd5b602085015b83811015611eda5780356001600160401b03811115611e3b575f5ffd5b86016080818903601f19011215611e50575f5ffd5b611e58611ad4565b602082013560038110611e69575f5ffd5b815260408201356affffffffffffffffffffff81168114611e88575f5ffd5b6020820152611e9960608301611a00565b604082015260808201356001600160401b03811115611eb6575f5ffd5b611ec58a602083860101611b48565b60608301525084525060209283019201611e1e565b5095945050505050565b5f5f5f5f60608587031215611ef7575f5ffd5b8435611f02816119ec565b935060208501356001600160401b03811115611f1c575f5ffd5b611f2887828801611a10565b90945092505060408501356001600160401b03811115611f46575f5ffd5b85016101208188031215611f58575f5ffd5b611f60611af6565b8135815260208083013590820152611f7a60408301611a00565b604082015260608201356001600160401b03811115611f97575f5ffd5b611fa389828501611b48565b606083015250611fb68860808401611bc9565b608082015260e08201356001600160401b03811115611fd3575f5ffd5b611fdf89828501611c5c565b60a0830152506101008201356001600160401b03811115611ffe575f5ffd5b61200a89828501611ddb565b60c08301525094979396509194505050565b5f6020828403121561202c575f5ffd5b5035919050565b5f60608201858352846020840152606060408401528084518083526080850191506020860192505f5b818110156120835783516001600160a01b031683526020938401939092019160010161205c565b5090979650505050505050565b5f602082840312156120a0575f5ffd5b81356120ab816119ec565b9392505050565b5f5f602083850312156120c3575f5ffd5b82356001600160401b038111156120d8575f5ffd5b6120e485828601611a10565b90969095509350505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f6120ab60208301846120f0565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b604081525f61216b604083018587612130565b905060018060a01b0383166020830152949350505050565b5f60208284031215612193575f5ffd5b815168ffffffffffffffffff811681146120ab575f5ffd5b634e487b7160e01b5f52601160045260245ffd5b81810381811115611658576116586121ab565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f81518060208401855e5f93019283525090919050565b5f61180461221f83866121fa565b846121fa565b5f6120ab82846121fa565b6001600160a01b03831681526040602080830182905283519183018290525f91908401906060840190835b8181101561227957835183526020938401939092019160010161225b565b50909695505050505050565b5f60208284031215612295575f5ffd5b815180151581146120ab575f5ffd5b80820180821115611658576116586121ab565b5f6122c282856121fa565b6101d160f51b81526122d760028201856121fa565b95945050505050565b5f5f5f5f608085870312156122f3575f5ffd5b845160048110612301575f5ffd5b602086015190945061231281611c3a565b604086015190935061232381611c48565b6060860151909250612334816119ec565b939692955090935050565b818382375f9101908152919050565b5f6020828403121561235e575f5ffd5b815162ffffff811681146120ab575f5ffd5b6001600160801b038181168382160290811690818114612392576123926121ab565b5092915050565b634e487b7160e01b5f52601260045260245ffd5b5f6001600160801b038316806123c5576123c5612399565b806001600160801b0384160491505092915050565b6001600160801b038281168282160390811115611658576116586121ab565b80516001600160a01b031682526020808201516001600160801b039081169184019190915260409182015116910152565b5f81516040845261243e60408501826120f0565b60208481015186830387830152805180845292935081019181840191600582901b8501015f5b828110156124e557601f19868303018452845160ff815116835260018060f81b03602082015116602084015260408101516040840152606081015160a060608501526124b360a08501826120f0565b90506080820151915083810360808501526124ce81836120f0565b602097880197969096019593505050600101612464565b50979650505050505050565b5f82825180855260208501945060208160051b830101602085015f5b8381101561227957848303601f19018852815180516003811061253e57634e487b7160e01b5f52602160045260245ffd5b84526020818101516affffffffffffffffffffff16908501526040808201516001600160a01b031690850152606090810151608091850182905290612585908501826120f0565b6020998a019990945092909201915060010161250d565b60c081525f6125ae60c08301876120f0565b6125bb60208401876123f9565b82810360808401526125cd818661242a565b905082810360a08401526125e181856124f1565b979650505050505050565b600181811c9082168061260057607f821691505b60208210810361261e57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561266b57805f5260205f20601f840160051c810160208510156126495750805b601f840160051c820191505b81811015612668575f8155600101612655565b50505b505050565b6001600160401b0383111561268757612687611a54565b61269b8361269583546125ec565b83612624565b5f601f8411600181146126cc575f85156126b55750838201355b5f19600387901b1c1916600186901b178355612668565b5f83815260208120601f198716915b828110156126fb57868501358255602094850194600190920191016126db565b5086821015612717575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b5f60208284031215612739575f5ffd5b5051919050565b60a081525f61275360a083018789612130565b856020840152828103806040850152600e82526d4563686f4d61726b65744461746160901b602083015285606085015260408101608085015250835160408201526020840151606082015260018060a01b036040850151166080820152606084015161012060a08301526127cb6101608301826120f0565b905060808501516127df60c08401826123f9565b5060a0850151828203603f19016101208401526127fc828261242a565b91505060c0850151603f198383030161014084015261281b82826124f1565b9a9950505050505050505050565b5f8261283757612837612399565b500490565b8082028115828204841417611658576116586121ab565b5f60018201612864576128646121ab565b5060010190565b602081525f611804602083018486612130565b5f606082840312801561288f575f5ffd5b50612898611a68565b82516001600160401b03811681146128ae575f5ffd5b815260208301516128be816119ec565b602082015260408301516128d1816119ec565b60408201529392505050565b5f602082840312156128ed575f5ffd5b81516120ab816119ec56fea264697066735822122031b2034a9c62e2c3b55a79565afdc450fb536d167a785e139895c3f80b32fed764736f6c634300081e003360e06040525f60c052348015610013575f5ffd5b5060405161564e38038061564e83398101604081905261003291610047565b6001600160a01b03166080523360a052610074565b5f60208284031215610057575f5ffd5b81516001600160a01b038116811461006d575f5ffd5b9392505050565b60805160a05160c0516155396101155f395f81816125a50152818161269e01528181612f7a0152818161313f0152818161316a0152818161319a015281816131c4015281816132360152818161326101526132c201525f81816101380152610ca501525f8181610202015281816117e001528181611883015281816119de01528181611ef60152818161271d015281816127c00152612bfe01526155395ff3fe608060405234801561000f575f5ffd5b50600436106100a6575f3560e01c80639bff4ada1161006e5780639bff4ada14610172578063aa8ff04e14610194578063b5ecf912146101c9578063b6967b3b146101ea578063cfce1a99146101fd578063efdbb63114610224575f5ffd5b806310cf02b9146100aa578063431facbc146100d7578063602747bb146100f9578063603ab8b71461011e57806395953ee114610133575b5f5ffd5b6100bd6100b8366004614074565b610237565b604080519283526020830191909152015b60405180910390f35b6100ea6100e536600461408f565b6102f7565b6040516100ce9392919061414e565b61010c61010736600461408f565b610781565b6040516100ce96959493929190614175565b61013161012c366004614202565b610c99565b005b61015a7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100ce565b610185610180366004614249565b610fa7565b6040516100ce93929190614277565b6101a76101a23660046142e8565b611533565b60408051941515855260208501939093529183015260608201526080016100ce565b6101dc6101d736600461408f565b6116f5565b6040516100ce929190614368565b6101316101f83660046143cb565b6117b3565b61015a7f000000000000000000000000000000000000000000000000000000000000000081565b610131610232366004614479565b61271a565b6001600160a01b0381165f908152600560205260409020546001600160801b03600160801b8204811691168115801561026e575080155b156102f2576040516310cf02b960e01b81526001600160a01b0384166004820152730dac89c5aa805ecd1967828331fb16576125c0ee906310cf02b9906024016040805180830381865afa1580156102c8573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102ec91906144f7565b90925090505b915091565b6003545f9081906060901561077a57600354600b11156104a6576003546001600160401b0381111561032b5761032b614519565b60405190808252806020026020018201604052801561037057816020015b604080518082019091525f8152606060208201528152602001906001900390816103495790505b5090505f5b60038054829061038790600190614541565b6103919190614541565b815481106103a1576103a1614554565b5f9182526020918290206040805180820190915260029092020180546001600160a01b0316825260018101805492939192918401916103df90614568565b80601f016020809104026020016040519081016040528092919081815260200182805461040b90614568565b80156104565780601f1061042d57610100808354040283529160200191610456565b820191905f5260205f20905b81548152906001019060200180831161043957829003601f168201915b50505050508152505082828151811061047157610471614554565b602090810291909101015260035461048b90600190614541565b811461049957600101610375565b600180935093505061077a565b835f036104b257600193505b6003546104c190600a906145b4565b91505f6104cf83600a6145d3565b6003546104dc9190614541565b6003549091506104ed84600a6145d3565b101561050157826104fd816145ea565b9350505b8285111561050d578294505b8493505f5f8660010361053d5760035461052990600190614541565b9150610536600a83614541565b90506105fa565b84870361056757821561055a57610555600184614541565b610560565b60099150815b91506105fa565b825f03610574575f61057f565b61057f600184614541565b6105898887614541565b61059490600a6145d3565b61059e9190614602565b6105a89083614602565b9150825f036105b7575f6105c2565b6105c2600184614541565b60016105ce8988614541565b6105d89190614541565b6105e390600a6145d3565b6105ed9190614602565b6105f79082614602565b90505b80610606836001614602565b6106109190614541565b6001600160401b0381111561062757610627614519565b60405190808252806020026020018201604052801561066c57816020015b604080518082019091525f8152606060208201528152602001906001900390816106455790505b5093505f5b6003838154811061068457610684614554565b5f9182526020918290206040805180820190915260029092020180546001600160a01b0316825260018101805492939192918401916106c290614568565b80601f01602080910402602001604051908101604052809291908181526020018280546106ee90614568565b80156107395780601f1061071057610100808354040283529160200191610739565b820191905f5260205f20905b81548152906001019060200180831161071c57829003601f168201915b50505050508152505085828151811061075457610754614554565b602002602001018190525081830315610775575f1990920191600101610671565b505050505b9193909250565b5f5f60605f5f6060730dac89c5aa805ecd1967828331fb16576125c0ee6001600160a01b031663431facbc886040518263ffffffff1660e01b81526004016107cb91815260200190565b5f60405180830381865afa1580156107e5573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261080c919081019061473e565b600354929850909650945015610c9057600354600b11156109bc576003546001600160401b0381111561084157610841614519565b60405190808252806020026020018201604052801561088657816020015b604080518082019091525f81526060602082015281526020019060019003908161085f5790505b5090505f5b60038054829061089d90600190614541565b6108a79190614541565b815481106108b7576108b7614554565b5f9182526020918290206040805180820190915260029092020180546001600160a01b0316825260018101805492939192918401916108f590614568565b80601f016020809104026020016040519081016040528092919081815260200182805461092190614568565b801561096c5780601f106109435761010080835404028352916020019161096c565b820191905f5260205f20905b81548152906001019060200180831161094f57829003601f168201915b50505050508152505082828151811061098757610987614554565b60209081029190910101526003546109a190600190614541565b81146109af5760010161088b565b5060019250829150610c90565b865f036109c857600196505b6003546109d790600a906145b4565b91505f6109e583600a6145d3565b6003546109f29190614541565b600354909150610a0384600a6145d3565b1015610a175782610a13816145ea565b9350505b82881115610a23578297505b8793505f5f89600103610a5357600354610a3f90600190614541565b9150610a4c600a83614541565b9050610b10565b848a03610a7d578215610a7057610a6b600184614541565b610a76565b60099150815b9150610b10565b825f03610a8a575f610a95565b610a95600184614541565b610a9f8b87614541565b610aaa90600a6145d3565b610ab49190614602565b610abe9083614602565b9150825f03610acd575f610ad8565b610ad8600184614541565b6001610ae48c88614541565b610aee9190614541565b610af990600a6145d3565b610b039190614602565b610b0d9082614602565b90505b80610b1c836001614602565b610b269190614541565b6001600160401b03811115610b3d57610b3d614519565b604051908082528060200260200182016040528015610b8257816020015b604080518082019091525f815260606020820152815260200190600190039081610b5b5790505b5093505f5b60038381548110610b9a57610b9a614554565b5f9182526020918290206040805180820190915260029092020180546001600160a01b031682526001810180549293919291840191610bd890614568565b80601f0160208091040260200160405190810160405280929190818152602001828054610c0490614568565b8015610c4f5780601f10610c2657610100808354040283529160200191610c4f565b820191905f5260205f20905b815481529060010190602001808311610c3257829003601f168201915b505050505081525050858281518110610c6a57610c6a614554565b602002602001018190525081830315610c8b575f1990920191600101610b87565b505050505b91939550919395565b5f6001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632c960900336001600160a01b03166306fdde036040518163ffffffff1660e01b81526004015f60405180830381865afa158015610d0d573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610d349190810190614859565b6040518263ffffffff1660e01b8152600401610d50919061488a565b606060405180830381865afa158015610d6b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d8f919061489c565b602001516001600160a01b031603610dba576040516395383ea160e01b815260040160405180910390fd5b60048383604051610dcc929190614918565b9081526040805160209281900383019020335f908152925290205460ff1615610e08576040516321947e9960e21b815260040160405180910390fd5b60036040518060400160405280336001600160a01b0316815260200185858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920182905250939094525050835460018082018655948252602091829020845160029092020180546001600160a01b0319166001600160a01b0390921691909117815590830151929390929083019150610ea99082614973565b505050600160048484604051610ec0929190614918565b9081526040805160209281900383019020335f908152908352818120805460ff1916941515949094179093556005825282206001908101805491820181558352912001610f0e838583614a2d565b50335f90815260056020526040812080546001600160801b03428116600160801b0291161790556001805491610f43836145ea565b9091555050335f8181526005602052604090819020600190810154905491519092429290917fe50964cbc470160e6c3556ba4ea3ba3e84778d99446d737c1d1a8a4c9c50eb0391610f9a9189918991899190614b0e565b60405180910390a4505050565b6001600160a01b0381165f908152600560205260408120600101548190606090820361105b57604051634dffa56d60e11b8152600481018690526001600160a01b0385166024820152730dac89c5aa805ecd1967828331fb16576125c0ee90639bff4ada906044015f60405180830381865afa158015611029573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526110509190810190614b66565b92509250925061152c565b6001600160a01b0384165f90815260056020526040902060010154600b1115611220576001600160a01b0384165f908152600560205260409020600101546001600160401b038111156110b0576110b0614519565b6040519080825280602002602001820160405280156110e357816020015b60608152602001906001900390816110ce5790505b5090505f5b6001600160a01b0385165f90815260056020526040902060019081018054909183916111149190614541565b61111e9190614541565b8154811061112e5761112e614554565b905f5260205f2001805461114190614568565b80601f016020809104026020016040519081016040528092919081815260200182805461116d90614568565b80156111b85780601f1061118f576101008083540402835291602001916111b8565b820191905f5260205f20905b81548152906001019060200180831161119b57829003601f168201915b50505050508282815181106111cf576111cf614554565b6020908102919091018101919091526001600160a01b0386165f9081526005909152604090206001908101546112059190614541565b8114611213576001016110e8565b600180935093505061152c565b845f0361122c57600194505b6001600160a01b0384165f9081526005602052604090206001015461125390600a906145b4565b91505f61126183600a6145d3565b6001600160a01b0386165f908152600560205260409020600101546112869190614541565b6001600160a01b0386165f908152600560205260409020600101549091506112af84600a6145d3565b10156112c357826112bf816145ea565b9350505b828611156112cf578295505b8593505f5f87600103611317576001600160a01b0387165f9081526005602052604090206001908101546113039190614541565b9150611310600a83614541565b90506113d4565b8488036113415782156113345761132f600184614541565b61133a565b60099150815b91506113d4565b825f0361134e575f611359565b611359600184614541565b6113638987614541565b61136e90600a6145d3565b6113789190614602565b6113829083614602565b9150825f03611391575f61139c565b61139c600184614541565b60016113a88a88614541565b6113b29190614541565b6113bd90600a6145d3565b6113c79190614602565b6113d19082614602565b90505b806113e0836001614602565b6113ea9190614541565b6001600160401b0381111561140157611401614519565b60405190808252806020026020018201604052801561143457816020015b606081526020019060019003908161141f5790505b5093505f5b6001600160a01b0388165f90815260056020526040902060010180548490811061146557611465614554565b905f5260205f2001805461147890614568565b80601f01602080910402602001604051908101604052809291908181526020018280546114a490614568565b80156114ef5780601f106114c6576101008083540402835291602001916114ef565b820191905f5260205f20905b8154815290600101906020018083116114d257829003601f168201915b505050505085828151811061150657611506614554565b602002602001018190525081830315611527575f1990920191600101611439565b505050505b9250925092565b5f5f5f5f60048888604051611549929190614918565b90815260408051602092819003830190206001600160a01b038c165f908152925290205460ff1693508315611660576004888860405161158a929190614918565b90815260408051602092819003830181206001600160a01b038d165f9081529352912054610100900460ff1693506004906115c8908a908a90614918565b90815260408051602092819003830190206001600160a01b038c165f90815292529020546201000090046001600160f01b03169150841561165b5760048888604051611615929190614918565b9081526040805191829003602090810183206001600160a01b038d165f908152915220600201906116499088908890614918565b90815260200160405180910390205490505b6116e9565b604051635547f82760e11b8152730dac89c5aa805ecd1967828331fb16576125c0ee9063aa8ff04e9061169f908c908c908c908c908c90600401614c1b565b608060405180830381865afa1580156116ba573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116de9190614c72565b929650909450925090505b95509550955095915050565b60038181548110611704575f80fd5b5f918252602090912060029091020180546001820180546001600160a01b0390921693509061173290614568565b80601f016020809104026020016040519081016040528092919081815260200182805461175e90614568565b80156117a95780601f10611780576101008083540402835291602001916117a9565b820191905f5260205f20905b81548152906001019060200180831161178c57829003601f168201915b5050505050905082565b821580156117bf575080155b156117dd57604051635b9e1d4560e11b815260040160405180910390fd5b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa15801561183a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061185e9190614cac565b604051632ac1326b60e01b815233600482018190526001600160a01b039283161492507f000000000000000000000000000000000000000000000000000000000000000090911690632ac1326b90602401602060405180830381865afa1580156118ca573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118ee9190614cc7565b80156118f8575080155b1561191657604051631e4230c760e21b815260040160405180910390fd5b60048787604051611928929190614918565b90815260408051602092819003830190206001600160a01b038b165f908152925290205460ff16611979578686604051633c00a7af60e01b8152600401611970929190614ce0565b60405180910390fd5b60608415611bf5575f5f5b86811015611bf25787878281811061199e5761199e614554565b90506020028101906119b09190614cf3565b6119ba9080614d11565b6040516020016119cb929190614d53565b60405160208183030381529060405292507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b6001e24600185604051602001611a1e9190614d8d565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b8152600401611a659291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa158015611a80573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611aa49190614dbe565b94505050506001600160a01b0382163314801590611ac0575083155b15611ae3578260405160016218620560e01b03198152600401611970919061488a565b878782818110611af557611af5614554565b9050602002810190611b079190614cf3565b611b15906020810190614d11565b611b1e91614e0b565b604051600490611b31908d908d90614918565b90815260200160405180910390205f8d6001600160a01b03166001600160a01b031681526020019081526020015f2060020184604051611b719190614d8d565b90815260405190819003602001902055336001600160a01b038c167fd7b65b61e10654aaf9ab3a7af2b11e30c5c7e3b2ce4dfe8378f8ca14687c2d1e8c8c8c8c87818110611bc157611bc1614554565b9050602002810190611bd39190614cf3565b604051611be293929190614e69565b60405180910390a3600101611984565b50505b821561270f575f896001600160a01b03166326f016376040518163ffffffff1660e01b81526004015f60405180830381865afa158015611c37573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611c5e9190810190614ecf565b5190508484905060048a8a604051611c77929190614918565b90815260408051602092819003830190206001600160a01b038e165f9081529252902054611cad90610100900460ff1683614541565b14158015611cb9575082155b15611cdb578888604051635170461160e11b8152600401611970929190614ce0565b5f5f5b858110156122a5575f878783818110611cf957611cf9614554565b9050602002810190611d0b9190615012565b611d19906020810190615026565b6002811115611d2a57611d2a614ffe565b03611d8857868682818110611d4157611d41614554565b9050602002810190611d539190615012565b611d61906040810190614d11565b604051602001611d72929190615041565b6040516020818303038152906040529350611e6a565b6001878783818110611d9c57611d9c614554565b9050602002810190611dae9190615012565b611dbc906020810190615026565b6002811115611dcd57611dcd614ffe565b03611e1557868682818110611de457611de4614554565b9050602002810190611df69190615012565b611e04906040810190614d11565b604051602001611d72929190615063565b868682818110611e2757611e27614554565b9050602002810190611e399190615012565b611e47906040810190614d11565b604051602001611e58929190615084565b60405160208183030381529060405293505b60405163aa588b4b60e01b81526001600160a01b038d169063aa588b4b90611e9690879060040161488a565b602060405180830381865afa158015611eb1573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ed59190614cc7565b611ef45783604051633cfbd52760e21b8152600401611970919061488a565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b6001e243386604051602001611f359190614d8d565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b8152600401611f7c9291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa158015611f97573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fbb9190614dbe565b5091935060029150611fca9050565b826003811115611fdc57611fdc614ffe565b14158015611fe8575084155b1561200b578360405160016218620560e01b03198152600401611970919061488a565b620186a087878381811061202157612021614554565b90506020028101906120339190615012565b602001351180612068575086868281811061205057612050614554565b90506020028101906120629190615012565b60200135155b156120b15786868281811061207f5761207f614554565b90506020028101906120919190615012565b602001356040516370e00d7760e01b815260040161197091815260200190565b60048b8b6040516120c3929190614918565b90815260200160405180910390205f8d6001600160a01b03166001600160a01b031681526020019081526020015f20600201846040516121039190614d8d565b9081526020016040518091039020545f1461213f578461213a578a8a604051635170461160e11b8152600401611970929190614ce0565b6121a4565b60048b8b604051612151929190614918565b90815260408051602092819003830190206001600160a01b038f165f908152925290208054610100900460ff1690600161218a836150a5565b91906101000a81548160ff021916908360ff160217905550505b8686828181106121b6576121b6614554565b90506020028101906121c89190615012565b6020013560048c8c6040516121de929190614918565b90815260200160405180910390205f8e6001600160a01b03166001600160a01b031681526020019081526020015f206002018560405161221e9190614d8d565b90815260405190819003602001902055336001600160a01b038d167fb72e5373934391ba6c4fc5b9542ee21c20a5d890a85cfdd16d446357e83af2888d8d888c8c8881811061226f5761226f614554565b90506020028101906122819190615012565b6020013560405161229594939291906150c3565b60405180910390a3600101611cde565b8260048c8c6040516122b8929190614918565b90815260200160405180910390205f8e6001600160a01b03166001600160a01b031681526020019081526020015f205f0160019054906101000a900460ff1660ff1603612654575f670de0b6b3a764000090505f8d6001600160a01b03166326f016376040518163ffffffff1660e01b81526004015f60405180830381865afa158015612347573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261236e9190810190614ecf565b90505f8d8d8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f81840152601f19601f8201169050808301925050505050505090505f8f90505f94505b82518510156125d5575f8386815181106123db576123db614554565b60200260200101515f015160028111156123f7576123f7614ffe565b036124405782858151811061240e5761240e614554565b60200260200101516060015160405160200161242a91906150fa565b60405160208183030381529060405297506124e3565b600183868151811061245457612454614554565b60200260200101515f0151600281111561247057612470614ffe565b036124a35782858151811061248757612487614554565b60200260200101516060015160405160200161242a9190615115565b8285815181106124b5576124b5614554565b6020026020010151606001516040516020016124d1919061512f565b60405160208183030381529060405297505b6125a161259a61252c8588815181106124fe576124fe614554565b602002602001015160200151662386f26fc1000061251c9190615149565b6affffffffffffffffffffff1690565b61259460048660405161253f9190614d8d565b9081526040805191829003602090810183206001600160a01b0389165f90815291522060020190612571908e90614d8d565b9081526020016040518091039020546509184e72a00061259191906145d3565b90565b906134a4565b85906135b2565b93507f000000000000000000000000000000000000000000000000000000000000000084146125d5576001909401936123bf565b836004836040516125e69190614d8d565b90815260408051602092819003830190206001600160a01b0385165f908152925290206001015583816001600160a01b03167ffd61a2d01d4536539eb3388a9803c27ef3a5b28bb51773f83534e77bf02770d784604051612647919061488a565b60405180910390a3505050505b6126c260048c8c604051612669929190614918565b90815260200160405180910390205f8e6001600160a01b03166001600160a01b031681526020019081526020015f20600101547f0000000000000000000000000000000000000000000000000000000000000000141590565b1561270b578b6001600160a01b03167ffb227420d8844fd0693b8044d6444d0d36e18e02034526f9d379c05612e719838c8c604051612702929190614ce0565b60405180910390a25b5050505b505050505050505050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa158015612777573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061279b9190614cac565b604051632ac1326b60e01b815233600482018190526001600160a01b039283161492507f000000000000000000000000000000000000000000000000000000000000000090911690632ac1326b90602401602060405180830381865afa158015612807573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061282b9190614cc7565b8015612835575080155b1561285357604051631e4230c760e21b815260040160405180910390fd5b60048484604051612865929190614918565b90815260408051602092819003830190206001600160a01b0388165f908152925290205460ff166128ad578383604051633c00a7af60e01b8152600401611970929190614ce0565b5f856001600160a01b031663ef8a92356040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128ea573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061290e9190615176565b9050600181600581111561292457612924614ffe565b141580156129445750600281600581111561294157612941614ffe565b14155b80156129625750600381600581111561295f5761295f614ffe565b14155b801561296c575081155b1561298a5760405163700f030f60e01b815260040160405180910390fd5b6129c96004868660405161299f929190614918565b90815260408051602092819003830190206001600160a01b038a165f908152925290206001015490565b5f036129e8576040516306b2e62960e11b815260040160405180910390fd5b5f866001600160a01b0316638c7a8eb36040518163ffffffff1660e01b81526004015f60405180830381865afa158015612a24573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052612a4b9190810190615194565b90505f612a588580614d11565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050604051929350612aa0928492506020019050614d8d565b60408051601f1981840301815290829052805160209182012084519092612ac79201614d8d565b6040516020818303038152906040528051906020012014612afb57604051637128647b60e11b815260040160405180910390fd5b5f60605f5b612b0d6020890189615333565b9050831015612e2c5783612b2460208a018a615333565b85818110612b3457612b34614554565b9050602002810190612b469190614cf3565b612b509080614d11565b604051602001612b6293929190615378565b60408051601f198184030181529082905263aa588b4b60e01b825292506001600160a01b038c169063aa588b4b90612b9e90859060040161488a565b602060405180830381865afa158015612bb9573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612bdd9190614cc7565b612bfc5781604051631f3f577f60e21b8152600401611970919061488a565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b6001e245f84604051602001612c3d9190614d8d565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b8152600401612c849291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa158015612c9f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612cc39190614dbe565b93505050336001600160a01b0383161480159150612cdf575086155b15612d02578160405160016218620560e01b03198152600401611970919061488a565b612d0f6020890189615333565b84818110612d1f57612d1f614554565b9050602002810190612d319190614cf3565b6020013560048b8b604051612d47929190614918565b90815260200160405180910390205f8d6001600160a01b03166001600160a01b031681526020019081526020015f2060020183604051612d879190614d8d565b908152602001604051809103902081905550336001600160a01b03168b6001600160a01b03167f9e9ec73da222bb34e89c444ee2841702e1509018ad901b4deb68b0ef9b859f398c8c42878e8060200190612de29190615333565b8b818110612df257612df2614554565b9050602002810190612e049190614cf3565b60200135604051612e199594939291906153a4565b60405180910390a3600190920191612b00565b6001600160a01b038b165f90815260056020526040812080546fffffffffffffffffffffffffffffffff1916426001600160801b03161790559250825f8b8b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152509293508f925050505b876020015151861015613081578688602001518781518110612ec857612ec8614554565b602002602001015160600151604051602001612ee59291906153e2565b6040516020818303038152906040529450600482604051612f069190614d8d565b9081526040805191829003602090810183206001600160a01b0385165f90815291522060020190612f38908790614d8d565b90815260200160405180910390205488602001518781518110612f5d57612f5d614554565b6020026020010151602001516001600160f81b03161115612fa0577f00000000000000000000000000000000000000000000000000000000000000009250613081565b61307461306d61302e612fd18b602001518a81518110612fc257612fc2614554565b60200260200101516040015190565b613028600487604051612fe49190614d8d565b9081526040805191829003602090810183206001600160a01b038a165f90815291522060020190613016908c90614d8d565b90815260200160405180910390205490565b906135c7565b6130678b602001518a8151811061304757613047614554565b60200260200101515f015160ff16662386f26fc1000061259191906145d3565b906135b2565b84906135de565b6001909601959250612ea4565b82816001600160a01b03167fc8d7d7296c924c3920078949d915bb20c80351022f3d90dda892192ad65cd974846040516130bb919061488a565b60405180910390a35f5f5f613134846001600160a01b03166324034a0e876040518263ffffffff1660e01b81526004016130f5919061488a565b602060405180830381865afa158015613110573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125919190615402565b92505f9150600190507f0000000000000000000000000000000000000000000000000000000000000000831415801561318c57507f000000000000000000000000000000000000000000000000000000000000000086145b1561319857505f6132c0565b7f0000000000000000000000000000000000000000000000000000000000000000831480156131e757507f00000000000000000000000000000000000000000000000000000000000000008614155b156132345761322d6004866040516131ff9190614d8d565b90815260408051602092819003830190206001600160a01b0388165f908152925290206001015487906135b2565b91506132c0565b7f0000000000000000000000000000000000000000000000000000000000000000831415801561328457507f00000000000000000000000000000000000000000000000000000000000000008614155b156132c05761329c6004866040516131ff9190614d8d565b9150818310156132b05761322d82846135ec565b6132ba83836135ec565b91505f90505b7f00000000000000000000000000000000000000000000000000000000000000008214613491575f8080806001600160a01b03881663a7c64c708a88886040518463ffffffff1660e01b815260040161331b93929190615419565b6080604051808303815f875af1158015613337573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061335b9190615442565b604051939750919550935091504290600490613378908c90614d8d565b90815260408051602092819003830190206001600160a01b038c165f90815292528120805461ffff16620100006001600160f01b0394909416939093029290921790915581900b156133fc57805f0b6001146133e65760028054905f6133dd8361547c565b919050556133fa565b60028054905f6133f5836145ea565b919050555b505b8461341d57855f80548190613412908490614541565b925050819055613435565b855f8054819061342e908490614602565b9250508190555b508286896001600160a01b03167fc75368439f060eb528fcbcf2dcce8dbca5f2e6a36d590b59a7d354d4c6ce96f28c898988425f600201545f5f01546040516134849796959493929190615491565b60405180910390a4505050505b5050505050505050505050505050505050565b5f82828183036134cd5780156134ba575f6134c4565b670de0b6b3a76400005b925050506135ac565b670de0b6b3a764000082036134ee57670de0b6b3a7640000925050506135ac565b805f0361350757670de0b6b3a7640000925050506135ac565b670de0b6b3a764000081036135205784925050506135ac565b670de0b6b3a76400008211156135515761354a61354561353f876135fa565b866135b2565b613721565b92506135a9565b5f61356e612591846ec097ce7bc90715b34b9f10000000006145b4565b90505f613586613545613580846135fa565b886135b2565b90506135a4612591826ec097ce7bc90715b34b9f10000000006145b4565b945050505b50505b92915050565b5f6135c0612591848461377d565b9392505050565b5f6135c061259184670de0b6b3a76400008561382f565b5f6135c06125918385614602565b5f6135c06125918385614541565b5f81670de0b6b3a76400008110156136285760405163036d32ef60e41b815260048101849052602401611970565b5f6136a9670de0b6b3a7640000830460016001600160801b03821160071b91821c6001600160401b03811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c60ff8111600390811b91821c600f811160021b90811c918211871b91821c969096119490961792909217171791909117919091171790565b9050670de0b6b3a7640000810282821c670de0b6b3a763ffff1981016136d25750949350505050565b671bc16d674ec800006706f05b59d3b200005b801561371557670de0b6b3a764000083800204925081831061370d579283019260019290921c915b60011c6136e5565b50919695505050505050565b5f81680a688906bd8affffff8111156137505760405163b3b6ba1f60e01b815260048101849052602401611970565b5f613767670de0b6b3a7640000604084901b6145b4565b9050613775612591826138fd565b949350505050565b5f80805f19848609848602925082811083820303915050805f036137ae5750670de0b6b3a7640000900490506135ac565b670de0b6b3a764000081106137e057604051635173648d60e01b81526004810186905260248101859052604401611970565b5f670de0b6b3a764000085870962040000818503049310909103600160ee1b02919091177faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106690291505092915050565b5f80805f19858709858702925082811083820303915050805f036138665783828161385c5761385c6145a0565b04925050506135c0565b83811061389757604051630c740aef60e31b8152600481018790526024810186905260448101859052606401611970565b5f84868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203025f889003889004909101858311909403939093029303949094049190911702949350505050565b600160bf1b67ff00000000000000821615613a0a576780000000000000008216156139315768016a09e667f3bcc9090260401c5b674000000000000000821615613950576801306fe0a31b7152df0260401c5b67200000000000000082161561396f576801172b83c7d517adce0260401c5b67100000000000000082161561398e5768010b5586cf9890f62a0260401c5b6708000000000000008216156139ad576801059b0d31585743ae0260401c5b6704000000000000008216156139cc57680102c9a3e778060ee70260401c5b6702000000000000008216156139eb5768010163da9fb33356d80260401c5b670100000000000000821615613a0a57680100b1afa5abcbed610260401c5b66ff000000000000821615613b09576680000000000000821615613a375768010058c86da1c09ea20260401c5b6640000000000000821615613a55576801002c605e2e8cec500260401c5b6620000000000000821615613a7357680100162f3904051fa10260401c5b6610000000000000821615613a91576801000b175effdc76ba0260401c5b6608000000000000821615613aaf57680100058ba01fb9f96d0260401c5b6604000000000000821615613acd5768010002c5cc37da94920260401c5b6602000000000000821615613aeb576801000162e525ee05470260401c5b6601000000000000821615613b095768010000b17255775c040260401c5b65ff0000000000821615613bff5765800000000000821615613b34576801000058b91b5bc9ae0260401c5b65400000000000821615613b5157680100002c5c89d5ec6d0260401c5b65200000000000821615613b6e5768010000162e43f4f8310260401c5b65100000000000821615613b8b57680100000b1721bcfc9a0260401c5b65080000000000821615613ba85768010000058b90cf1e6e0260401c5b65040000000000821615613bc5576801000002c5c863b73f0260401c5b65020000000000821615613be257680100000162e430e5a20260401c5b65010000000000821615613bff576801000000b1721835510260401c5b64ff00000000821615613cec57648000000000821615613c2857680100000058b90c0b490260401c5b644000000000821615613c445768010000002c5c8601cc0260401c5b642000000000821615613c60576801000000162e42fff00260401c5b641000000000821615613c7c5768010000000b17217fbb0260401c5b640800000000821615613c98576801000000058b90bfce0260401c5b640400000000821615613cb457680100000002c5c85fe30260401c5b640200000000821615613cd05768010000000162e42ff10260401c5b640100000000821615613cec57680100000000b17217f80260401c5b63ff000000821615613dd0576380000000821615613d135768010000000058b90bfc0260401c5b6340000000821615613d2e576801000000002c5c85fe0260401c5b6320000000821615613d4957680100000000162e42ff0260401c5b6310000000821615613d64576801000000000b17217f0260401c5b6308000000821615613d7f57680100000000058b90c00260401c5b6304000000821615613d9a5768010000000002c5c8600260401c5b6302000000821615613db5576801000000000162e4300260401c5b6301000000821615613dd05768010000000000b172180260401c5b62ff0000821615613eab5762800000821615613df5576801000000000058b90c0260401c5b62400000821615613e0f57680100000000002c5c860260401c5b62200000821615613e295768010000000000162e430260401c5b62100000821615613e4357680100000000000b17210260401c5b62080000821615613e5d5768010000000000058b910260401c5b62040000821615613e77576801000000000002c5c80260401c5b62020000821615613e9157680100000000000162e40260401c5b62010000821615613eab576801000000000000b1720260401c5b61ff00821615613f7d57618000821615613ece57680100000000000058b90260401c5b614000821615613ee75768010000000000002c5d0260401c5b612000821615613f00576801000000000000162e0260401c5b611000821615613f195768010000000000000b170260401c5b610800821615613f32576801000000000000058c0260401c5b610400821615613f4b57680100000000000002c60260401c5b610200821615613f6457680100000000000001630260401c5b610100821615613f7d57680100000000000000b10260401c5b60ff821615614046576080821615613f9e57680100000000000000590260401c5b6040821615613fb6576801000000000000002c0260401c5b6020821615613fce57680100000000000000160260401c5b6010821615613fe6576801000000000000000b0260401c5b6008821615613ffe57680100000000000000060260401c5b600482161561401657680100000000000000030260401c5b600282161561402e57680100000000000000010260401c5b600182161561404657680100000000000000010260401c5b670de0b6b3a76400000260409190911c60bf031c90565b6001600160a01b0381168114614071575f5ffd5b50565b5f60208284031215614084575f5ffd5b81356135c08161405d565b5f6020828403121561409f575f5ffd5b5035919050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b5f82825180855260208501945060208160051b830101602085015f5b8381101561414257848303601f19018852815180516001600160a01b0316845260209081015160409185018290529061412b908501826140a6565b6020998a01999094509290920191506001016140f0565b50909695505050505050565b838152826020820152606060408201525f61416c60608301846140d4565b95945050505050565b86815285602082015260c060408201525f61419360c08301876140d4565b85606084015284608084015282810360a08401526141b181856140d4565b9998505050505050505050565b5f5f83601f8401126141ce575f5ffd5b5081356001600160401b038111156141e4575f5ffd5b6020830191508360208285010111156141fb575f5ffd5b9250929050565b5f5f5f60408486031215614214575f5ffd5b83356001600160401b03811115614229575f5ffd5b614235868287016141be565b909790965060209590950135949350505050565b5f5f6040838503121561425a575f5ffd5b82359150602083013561426c8161405d565b809150509250929050565b5f606082018583528460208401526060604084015280845180835260808501915060808160051b8601019250602086015f5b828110156142da57607f198786030184526142c58583516140a6565b945060209384019391909101906001016142a9565b509298975050505050505050565b5f5f5f5f5f606086880312156142fc575f5ffd5b85356143078161405d565b945060208601356001600160401b03811115614321575f5ffd5b61432d888289016141be565b90955093505060408601356001600160401b0381111561434b575f5ffd5b614357888289016141be565b969995985093965092949392505050565b6001600160a01b03831681526040602082018190525f90613775908301846140a6565b5f5f83601f84011261439b575f5ffd5b5081356001600160401b038111156143b1575f5ffd5b6020830191508360208260051b85010111156141fb575f5ffd5b5f5f5f5f5f5f5f6080888a0312156143e1575f5ffd5b87356143ec8161405d565b965060208801356001600160401b03811115614406575f5ffd5b6144128a828b016141be565b90975095505060408801356001600160401b03811115614430575f5ffd5b61443c8a828b0161438b565b90955093505060608801356001600160401b0381111561445a575f5ffd5b6144668a828b0161438b565b989b979a50959850939692959293505050565b5f5f5f5f6060858703121561448c575f5ffd5b84356144978161405d565b935060208501356001600160401b038111156144b1575f5ffd5b6144bd878288016141be565b90945092505060408501356001600160401b038111156144db575f5ffd5b8501604081880312156144ec575f5ffd5b939692955090935050565b5f5f60408385031215614508575f5ffd5b505080516020909101519092909150565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b818103818111156135ac576135ac61452d565b634e487b7160e01b5f52603260045260245ffd5b600181811c9082168061457c57607f821691505b60208210810361459a57634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52601260045260245ffd5b5f826145ce57634e487b7160e01b5f52601260045260245ffd5b500490565b80820281158282048414176135ac576135ac61452d565b5f600182016145fb576145fb61452d565b5060010190565b808201808211156135ac576135ac61452d565b604080519081016001600160401b038111828210171561463757614637614519565b60405290565b604051608081016001600160401b038111828210171561463757614637614519565b60405160a081016001600160401b038111828210171561463757614637614519565b604051601f8201601f191681016001600160401b03811182821017156146a9576146a9614519565b604052919050565b5f6001600160401b038211156146c9576146c9614519565b5060051b60200190565b5f82601f8301126146e2575f5ffd5b81516001600160401b038111156146fb576146fb614519565b61470e601f8201601f1916602001614681565b818152846020838601011115614722575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f5f5f60608486031215614750575f5ffd5b83516020850151604086015191945092506001600160401b03811115614774575f5ffd5b8401601f81018613614784575f5ffd5b8051614797614792826146b1565b614681565b8082825260208201915060208360051b8501019250888311156147b8575f5ffd5b602084015b8381101561484a5780516001600160401b038111156147da575f5ffd5b85016040818c03601f190112156147ef575f5ffd5b6147f7614615565b60208201516148058161405d565b815260408201516001600160401b0381111561481f575f5ffd5b61482e8d6020838601016146d3565b60208301525080855250506020830192506020810190506147bd565b50809450505050509250925092565b5f60208284031215614869575f5ffd5b81516001600160401b0381111561487e575f5ffd5b613775848285016146d3565b602081525f6135c060208301846140a6565b5f60608284031280156148ad575f5ffd5b50604051606081016001600160401b03811182821017156148d0576148d0614519565b60405282516001600160401b03811681146148e9575f5ffd5b815260208301516148f98161405d565b6020820152604083015161490c8161405d565b60408201529392505050565b818382375f9101908152919050565b601f82111561496e57805f5260205f20601f840160051c8101602085101561494c5750805b601f840160051c820191505b8181101561496b575f8155600101614958565b50505b505050565b81516001600160401b0381111561498c5761498c614519565b6149a08161499a8454614568565b84614927565b6020601f8211600181146149d2575f83156149bb5750848201515b5f19600385901b1c1916600184901b17845561496b565b5f84815260208120601f198516915b82811015614a0157878501518255602094850194600190920191016149e1565b5084821015614a1e57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6001600160401b03831115614a4457614a44614519565b614a5883614a528354614568565b83614927565b5f601f841160018114614a89575f8515614a725750838201355b5f19600387901b1c1916600186901b17835561496b565b5f83815260208120601f198716915b82811015614ab85786850135825560209485019460019092019101614a98565b5086821015614ad4575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f614b21608083018688614ae6565b8460208401528281036040840152614b5481600e81526d4563686f4d61726b65744461746160901b602082015260400190565b91505082606083015295945050505050565b5f5f5f60608486031215614b78575f5ffd5b83516020850151604086015191945092506001600160401b03811115614b9c575f5ffd5b8401601f81018613614bac575f5ffd5b8051614bba614792826146b1565b8082825260208201915060208360051b850101925088831115614bdb575f5ffd5b602084015b8381101561484a5780516001600160401b03811115614bfd575f5ffd5b614c0c8b6020838901016146d3565b84525060209283019201614be0565b6001600160a01b03861681526060602082018190525f90614c3f9083018688614ae6565b8281036040840152614c52818587614ae6565b98975050505050505050565b80518015158114614c6d575f5ffd5b919050565b5f5f5f5f60808587031215614c85575f5ffd5b614c8e85614c5e565b60208601516040870151606090970151919890975090945092505050565b5f60208284031215614cbc575f5ffd5b81516135c08161405d565b5f60208284031215614cd7575f5ffd5b6135c082614c5e565b602081525f613775602083018486614ae6565b5f8235603e19833603018112614d07575f5ffd5b9190910192915050565b5f5f8335601e19843603018112614d26575f5ffd5b8301803591506001600160401b03821115614d3f575f5ffd5b6020019150368190038213156141fb575f5ffd5b67021a7a72324a39d160c51b8152818360088301375f9101600801908152919050565b5f81518060208401855e5f93019283525090919050565b5f6135c08284614d76565b805160ff81168114614c6d575f5ffd5b80516001600160f81b0381168114614c6d575f5ffd5b5f5f5f5f60808587031215614dd1575f5ffd5b845160048110614ddf575f5ffd5b9350614ded60208601614d98565b9250614dfb60408601614da8565b915060608501516144ec8161405d565b803560208310156135ac575f19602084900360031b1b1692915050565b5f5f8335601e19843603018112614e3d575f5ffd5b83016020810192503590506001600160401b03811115614e5b575f5ffd5b8036038213156141fb575f5ffd5b604081525f614e7c604083018587614ae6565b8281036020840152614e8e8485614e28565b60408352614ea0604084018284614ae6565b915050614eb06020860186614e28565b83830360208501526141b1838284614ae6565b60038110614071575f5ffd5b5f60208284031215614edf575f5ffd5b81516001600160401b03811115614ef4575f5ffd5b8201601f81018413614f04575f5ffd5b8051614f12614792826146b1565b8082825260208201915060208360051b850101925086831115614f33575f5ffd5b602084015b83811015614ff35780516001600160401b03811115614f55575f5ffd5b85016080818a03601f19011215614f6a575f5ffd5b614f7261463d565b6020820151614f8081614ec3565b815260408201516affffffffffffffffffffff81168114614f9f575f5ffd5b60208201526060820151614fb28161405d565b604082015260808201516001600160401b03811115614fcf575f5ffd5b614fde8b6020838601016146d3565b60608301525084525060209283019201614f38565b509695505050505050565b634e487b7160e01b5f52602160045260245ffd5b5f8235605e19833603018112614d07575f5ffd5b5f60208284031215615036575f5ffd5b81356135c081614ec3565b66028a096a0a49d160cd1b8152818360078301375f9101600701908152919050565b65028a096a19d160d51b8152818360068301375f9101600601908152919050565b65028a096ab1d160d51b8152818360068301375f9101600601908152919050565b5f60ff821660ff81036150ba576150ba61452d565b60010192915050565b606081525f6150d6606083018688614ae6565b82810360208401526150e881866140a6565b91505082604083015295945050505050565b66028a096a0a49d160cd1b81525f6135c06007830184614d76565b65028a096a19d160d51b81525f6135c06006830184614d76565b65028a096ab1d160d51b81525f6135c06006830184614d76565b6affffffffffffffffffffff818116838216029081169081811461516f5761516f61452d565b5092915050565b5f60208284031215615186575f5ffd5b8151600681106135c0575f5ffd5b5f602082840312156151a4575f5ffd5b81516001600160401b038111156151b9575f5ffd5b8201604081850312156151ca575f5ffd5b6151d2614615565b81516001600160401b038111156151e7575f5ffd5b6151f3868285016146d3565b82525060208201516001600160401b0381111561520e575f5ffd5b80830192505084601f830112615222575f5ffd5b8151615230614792826146b1565b8082825260208201915060208360051b860101925087831115615251575f5ffd5b602085015b838110156153225780516001600160401b03811115615273575f5ffd5b860160a0818b03601f19011215615288575f5ffd5b61529061465f565b61529c60208301614d98565b81526152aa60408301614da8565b60208201526060820151604082015260808201516001600160401b038111156152d1575f5ffd5b6152e08c6020838601016146d3565b60608301525060a08201516001600160401b038111156152fe575f5ffd5b61530d8c6020838601016146d3565b60808301525084525060209283019201615256565b506020840152509095945050505050565b5f5f8335601e19843603018112615348575f5ffd5b8301803591506001600160401b03821115615361575f5ffd5b6020019150600581901b36038213156141fb575f5ffd5b5f6153838286614d76565b6101d160f51b8152838560028301375f930160020192835250909392505050565b608081525f6153b7608083018789614ae6565b85602084015282810360408401526153cf81866140a6565b9150508260608301529695505050505050565b5f6153ed8285614d76565b6101d160f51b815261416c6002820185614d76565b5f60208284031215615412575f5ffd5b5051919050565b606081525f61542b60608301866140a6565b602083019490945250901515604090910152919050565b5f5f5f5f60808587031215615455575f5ffd5b845160208601516040870151606088015192965090945092505f81900b81146144ec575f5ffd5b5f8161548a5761548a61452d565b505f190190565b61010081525f6154a561010083018a6140a6565b881515602084015287604084015286606084015285608084015282810360a08401526154ec81600e81526d4563686f4d61726b65744461746160901b602082015260400190565b60c0840195909552505060e001529594505050505056fea26469706673582212204a253343498f310db1c3cf206b7fde058da7c332f4ba1f537594f38c3b351b3664736f6c634300081e0033000000000000000000000000c64f8f0c754bc41a29f95946fa0abb3828c47e6d000000000000000000000000be852f3b38f65d5e251a3127c25dc21fa9a9129e
Deployed Bytecode
0x608060405234801561000f575f5ffd5b5060043610610090575f3560e01c80635c60da1b116100635780635c60da1b1461014a5780639b5e6a8b1461015c578063a509f4d114610183578063cfce1a9914610196578063fe4d5536146101bd575f5ffd5b80630897d6f0146100945780630caba05e146100c457806317a68dd8146100e65780632c960900146100fb575b5f5ffd5b6100a76100a2366004611ee4565b6101dd565b6040516001600160a01b0390911681526020015b60405180910390f35b6100d76100d236600461201c565b611162565b6040516100bb93929190612033565b6100f96100f4366004612090565b611484565b005b61010e6101093660046120b2565b61151f565b6040805182516001600160401b031681526020808401516001600160a01b039081169183019190915292820151909216908201526060016100bb565b5f546100a7906001600160a01b031681565b6100a77f000000000000000000000000428e11662bc9c72d966e2089c19b13d26527342881565b6100a761019136600461201c565b61165e565b6100a77f000000000000000000000000c64f8f0c754bc41a29f95946fa0abb3828c47e6d81565b6101d06101cb36600461201c565b611686565b6040516100bb919061211e565b5f6101e78561171d565b6080820151602001516001600160801b0316156102105761020b826040015161171d565b610217565b5f60408301525b60088310156102395760405163a9cac3f760e01b815260040160405180910390fd5b604083111561025b57604051638af6981560e01b815260040160405180910390fd5b8160600151515f036102805760405163d57e933160e01b815260040160405180910390fd5b5f61028b858561151f565b602001516001600160a01b0316146102d25783836102a9868661151f565b602001516040516307bbbbbf60e21b81526004016102c993929190612158565b60405180910390fd5b81514211156102f457604051634ef659b360e11b815260040160405180910390fd5b815160208301511161031957604051634d9b1b8b60e01b815260040160405180910390fd5b7f000000000000000000000000c64f8f0c754bc41a29f95946fa0abb3828c47e6d6001600160a01b031663bec4d54e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610375573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103999190612183565b68ffffffffffffffffff161580159061044b5750815160208301516103be91906121bf565b7f000000000000000000000000c64f8f0c754bc41a29f95946fa0abb3828c47e6d6001600160a01b031663bec4d54e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561041a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061043e9190612183565b68ffffffffffffffffff16105b156104f7577f000000000000000000000000c64f8f0c754bc41a29f95946fa0abb3828c47e6d6001600160a01b031663bec4d54e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104ac573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104d09190612183565b604051637d1cd12360e11b815268ffffffffffffffffff90911660048201526024016102c9565b60808201515161050690611747565b6080820151602001516001600160801b03161580159061054757508160800151602001516001600160801b03168260800151604001516001600160801b0316105b15610565576040516308cd810560e01b815260040160405180910390fd5b8160c00151515f0361058a5760405163656f350160e01b815260040160405180910390fd5b6040805160018082528183019092525f918291606091839190602080830190803683370190505090505b8560c001515183101561087a575f8660c0015184815181106105d8576105d86121e6565b60200260200101515f015160028111156105f4576105f46121d2565b036106205760405180604001604052806007815260200166028a096a0a49d160cd1b81525091506106a1565b60018660c001518481518110610638576106386121e6565b60200260200101515f01516002811115610654576106546121d2565b0361067f5760405180604001604052806006815260200165028a096a19d160d51b81525091506106a1565b60405180604001604052806006815260200165028a096ab1d160d51b81525091505b818660c0015184815181106106b8576106b86121e6565b6020026020010151606001516040516020016106d5929190612211565b60408051601f19818403018152908290526106f291602001612225565b60405160208183030381529060405280519060200120815f8151811061071a5761071a6121e6565b6020026020010181815250507f000000000000000000000000c64f8f0c754bc41a29f95946fa0abb3828c47e6d6001600160a01b031663e6e2be168760c00151858151811061076b5761076b6121e6565b602002602001015160400151836040518363ffffffff1660e01b8152600401610795929190612230565b602060405180830381865afa1580156107b0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107d49190612285565b61083357818660c0015184815181106107ef576107ef6121e6565b60200260200101516060015160405160200161080c929190612211565b60408051601f1981840301815290829052633ddcaa5960e11b82526102c99160040161211e565b8560c001518381518110610849576108496121e6565b6020026020010151602001516affffffffffffffffffffff168461086d91906122a4565b93506001909201916105b4565b8360641461089e5760405163ecf0160d60e01b8152600481018590526024016102c9565b5f92505f93508560a0015160200151515f036108cd576040516379a7f53760e01b815260040160405180910390fd5b5f8660a0015160200151516001600160401b038111156108ef576108ef611a54565b604051908082528060200260200182016040528015610918578160200160208202803683370190505b5090505b8660a001516020015151841015610b55578660a00151602001518481518110610947576109476121e6565b60200260200101515f015160ff168561096091906122a4565b94508660a001515f01518760a00151602001518581518110610984576109846121e6565b6020026020010151606001516040516020016109a19291906122b7565b60408051601f19818403018152908290526109be91602001612225565b604051602081830303815290604052805190602001208185815181106109e6576109e66121e6565b6020026020010181815250505f7f000000000000000000000000c64f8f0c754bc41a29f95946fa0abb3828c47e6d6001600160a01b031663b6001e245f848881518110610a3557610a356121e6565b60200260200101516040518363ffffffff1660e01b8152600401610a6e9291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa158015610a89573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610aad91906122e0565b505060ff16915050805f03610b1f5760a08801518051602090910151805187908110610adb57610adb6121e6565b602002602001015160600151604051602001610af89291906122b7565b60408051601f198184030181529082905263ba7fb2ed60e01b82526102c99160040161211e565b808860a00151602001518681518110610b3a57610b3a6121e6565b6020908102919091010151604001525060019093019261091c565b84606414610b7957604051630d738e1160e31b8152600481018690526024016102c9565b6040516373715f0b60e11b81526001600160a01b037f000000000000000000000000c64f8f0c754bc41a29f95946fa0abb3828c47e6d169063e6e2be1690610bc7905f908590600401612230565b602060405180830381865afa158015610be2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c069190612285565b610c2357604051630361692160e51b815260040160405180910390fd5b610d155f5f9054906101000a90046001600160a01b03167f000000000000000000000000c64f8f0c754bc41a29f95946fa0abb3828c47e6d308d8b604001518e8e604051610c7292919061233f565b60405180910390205f1c8d5f01518e60200151604051602001610ce99796959493929190606097881b6bffffffffffffffffffffffff19908116825296881b8716601482015294871b861660288601529290951b909316603c83015260508201929092526070810192909252609082015260b00190565b6040516020818303038152906040528b8b604051610d0892919061233f565b60405180910390206117f7565b95505f620186a07f000000000000000000000000c64f8f0c754bc41a29f95946fa0abb3828c47e6d6001600160a01b0316638ddc321f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d78573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d9c919061234e565b62ffffff16896080015160400151610db49190612370565b610dbe91906123ad565b9050808860800151604001818151610dd691906123da565b6001600160801b03169052506060880151608089015160a08a015160c08b0151604051631d00132960e31b81526001600160a01b038c169463e800994894610e24949193909260040161259c565b5f604051808303815f87803b158015610e3b575f5ffd5b505af1158015610e4d573d5f5f3e3d5ffd5b505050506080880151516001600160a01b03166323b872dd33610e6e61180c565b6040516001600160e01b031960e085901b1681526001600160a01b039283166004820152911660248201526001600160801b03841660448201526064016020604051808303815f875af1158015610ec7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610eeb9190612285565b506080880151805160409182015191516323b872dd60e01b81523360048201526001600160a01b038a811660248301526001600160801b0390931660448201529116906323b872dd906064016020604051808303815f875af1158015610f53573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f779190612285565b50604080516060810182526001600160401b03421681526001600160a01b03808a1660208301525f5416818301529051600390610fb7908d908d9061233f565b908152604080516020928190038301812084518154948601516001600160401b039091166001600160e01b031990951694909417600160401b6001600160a01b03958616021781559390910151600190930180546001600160a01b031916939092169290921790558a908a906002905f90611035908590859061233f565b604051809103902081526020019081526020015f209182611057929190612670565b506001805480820182555f9182527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b0319166001600160a01b038a81169182179092559154604080516304cbb47f60e01b8152905191831693928f1692917f95f1e03f5a5037130736d042aaa0cf4d85d403655b81b55612c57126f636fa92918f918f9185916304cbb47f916004808201926020929091908290030181865afa158015611110573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111349190612729565b6001805490508f60405161114c959493929190612740565b60405180910390a4505050505050949350505050565b6001545f9081906060901561147d57600154600b111561125d576001546001600160401b0381111561119657611196611a54565b6040519080825280602002602001820160405280156111bf578160200160208202803683370190505b5090505f5b6001805482906111d59083906121bf565b6111df91906121bf565b815481106111ef576111ef6121e6565b905f5260205f20015f9054906101000a90046001600160a01b031682828151811061121c5761121c6121e6565b6001600160a01b03909216602092830291909101909101526001805461124291906121bf565b8114611250576001016111c4565b600180935093505061147d565b835f0361126957600193505b60015461127890600a90612829565b91505f61128683600a61283c565b60015461129391906121bf565b6001549091506112a484600a61283c565b10156112b857826112b481612853565b9350505b828511156112c4578294505b8493505f5f866001036112f357600180546112df91906121bf565b91506112ec600a836121bf565b90506113b0565b84870361131d5782156113105761130b6001846121bf565b611316565b60099150815b91506113b0565b825f0361132a575f611335565b6113356001846121bf565b61133f88876121bf565b61134a90600a61283c565b61135491906122a4565b61135e90836122a4565b9150825f0361136d575f611378565b6113786001846121bf565b600161138489886121bf565b61138e91906121bf565b61139990600a61283c565b6113a391906122a4565b6113ad90826122a4565b90505b806113bc8360016122a4565b6113c691906121bf565b6001600160401b038111156113dd576113dd611a54565b604051908082528060200260200182016040528015611406578160200160208202803683370190505b5093505f5b6001838154811061141e5761141e6121e6565b905f5260205f20015f9054906101000a90046001600160a01b031685828151811061144b5761144b6121e6565b6001600160a01b0390921660209283029190910190910152828214611478575f199092019160010161140b565b505050505b9193909250565b61148c61180c565b6001600160a01b0316336001600160a01b0316146114bd576040516315cead3960e31b815260040160405180910390fd5b6114c68161171d565b5f80546040516001600160a01b03808516939216917fcfbf4028add9318bbf716f08c348595afb063b0e9feed1f86d33681a4b3ed4d391a35f80546001600160a01b0319166001600160a01b0392909216919091179055565b604080516060810182525f80825260208201819052918101919091525f6001600160a01b03166003848460405161155792919061233f565b908152604051908190036020019020546001600160a01b03600160401b90910416036115fb57604051622c960960e81b8152738f1e629536c74e8d8f58cfcbb44ad9cdf66a243390632c960900906115b5908690869060040161286b565b606060405180830381865afa1580156115d0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115f4919061287e565b9050611658565b6003838360405161160d92919061233f565b90815260408051918290036020908101832060608401835280546001600160401b03811685526001600160a01b03600160401b90910481169285019290925260010154169082015290505b92915050565b6001818154811061166d575f80fd5b5f918252602090912001546001600160a01b0316905081565b60026020525f90815260409020805461169e906125ec565b80601f01602080910402602001604051908101604052809291908181526020018280546116ca906125ec565b80156117155780601f106116ec57610100808354040283529160200191611715565b820191905f5260205f20905b8154815290600101906020018083116116f857829003601f168201915b505050505081565b6001600160a01b0381166117445760405163140efc4d60e21b815260040160405180910390fd5b50565b6040516338b317cf60e21b81526001600160a01b0382811660048301527f000000000000000000000000c64f8f0c754bc41a29f95946fa0abb3828c47e6d169063e2cc5f3c90602401602060405180830381865afa1580156117ab573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117cf9190612285565b6117445760405163c1c1013b60e01b81526001600160a01b03821660048201526024016102c9565b5f6118048484845f611892565b949350505050565b5f7f000000000000000000000000c64f8f0c754bc41a29f95946fa0abb3828c47e6d6001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa158015611869573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061188d91906128dd565b905090565b8251604051613d6160f01b8152603a820160f090811b6002838101919091526680600b3d3981f360c81b600484015264363d3d376160d81b600b8401528301901b6010820181905268603836393d3d3d366160b81b6012830152601b82015262013d7360e81b601d820152606086901b6020808301919091526e5af43d82803e903d91603657fd5bf360881b60348301525f929160458301918390604383019089015b602083106119545780518252601f199092019160209182019101611935565b5f6001846020036101000a0319905080825116835283830192508660f01b83526f67363d3d37363d34f03d5260086018f35f52896010805ff58061199f5763ebfef1885f526004601cfd5b8060145261d6945f5260016034536001600160a01b036017601e201698505f5f88888d855af115893b1517156119dc57638f86d2f15f526004601cfd5b5050505050505050949350505050565b6001600160a01b0381168114611744575f5ffd5b8035611a0b816119ec565b919050565b5f5f83601f840112611a20575f5ffd5b5081356001600160401b03811115611a36575f5ffd5b602083019150836020828501011115611a4d575f5ffd5b9250929050565b634e487b7160e01b5f52604160045260245ffd5b604051606081016001600160401b0381118282101715611a8a57611a8a611a54565b60405290565b604080519081016001600160401b0381118282101715611a8a57611a8a611a54565b60405160a081016001600160401b0381118282101715611a8a57611a8a611a54565b604051608081016001600160401b0381118282101715611a8a57611a8a611a54565b60405160e081016001600160401b0381118282101715611a8a57611a8a611a54565b604051601f8201601f191681016001600160401b0381118282101715611b4057611b40611a54565b604052919050565b5f82601f830112611b57575f5ffd5b81356001600160401b03811115611b7057611b70611a54565b611b83601f8201601f1916602001611b18565b818152846020838601011115611b97575f5ffd5b816020850160208301375f918101602001919091529392505050565b80356001600160801b0381168114611a0b575f5ffd5b5f60608284031215611bd9575f5ffd5b611be1611a68565b90508135611bee816119ec565b8152611bfc60208301611bb3565b6020820152611c0d60408301611bb3565b604082015292915050565b5f6001600160401b03821115611c3057611c30611a54565b5060051b60200190565b60ff81168114611744575f5ffd5b6001600160f81b0381168114611744575f5ffd5b5f60408284031215611c6c575f5ffd5b611c74611a90565b905081356001600160401b03811115611c8b575f5ffd5b611c9784828501611b48565b82525060208201356001600160401b03811115611cb2575f5ffd5b8201601f81018413611cc2575f5ffd5b8035611cd5611cd082611c18565b611b18565b8082825260208201915060208360051b850101925086831115611cf6575f5ffd5b602084015b83811015611dcb5780356001600160401b03811115611d18575f5ffd5b850160a0818a03601f19011215611d2d575f5ffd5b611d35611ab2565b6020820135611d4381611c3a565b81526040820135611d5381611c48565b60208201526060820135604082015260808201356001600160401b03811115611d7a575f5ffd5b611d898b602083860101611b48565b60608301525060a08201356001600160401b03811115611da7575f5ffd5b611db68b602083860101611b48565b60808301525084525060209283019201611cfb565b5060208501525091949350505050565b5f82601f830112611dea575f5ffd5b8135611df8611cd082611c18565b8082825260208201915060208360051b860101925085831115611e19575f5ffd5b602085015b83811015611eda5780356001600160401b03811115611e3b575f5ffd5b86016080818903601f19011215611e50575f5ffd5b611e58611ad4565b602082013560038110611e69575f5ffd5b815260408201356affffffffffffffffffffff81168114611e88575f5ffd5b6020820152611e9960608301611a00565b604082015260808201356001600160401b03811115611eb6575f5ffd5b611ec58a602083860101611b48565b60608301525084525060209283019201611e1e565b5095945050505050565b5f5f5f5f60608587031215611ef7575f5ffd5b8435611f02816119ec565b935060208501356001600160401b03811115611f1c575f5ffd5b611f2887828801611a10565b90945092505060408501356001600160401b03811115611f46575f5ffd5b85016101208188031215611f58575f5ffd5b611f60611af6565b8135815260208083013590820152611f7a60408301611a00565b604082015260608201356001600160401b03811115611f97575f5ffd5b611fa389828501611b48565b606083015250611fb68860808401611bc9565b608082015260e08201356001600160401b03811115611fd3575f5ffd5b611fdf89828501611c5c565b60a0830152506101008201356001600160401b03811115611ffe575f5ffd5b61200a89828501611ddb565b60c08301525094979396509194505050565b5f6020828403121561202c575f5ffd5b5035919050565b5f60608201858352846020840152606060408401528084518083526080850191506020860192505f5b818110156120835783516001600160a01b031683526020938401939092019160010161205c565b5090979650505050505050565b5f602082840312156120a0575f5ffd5b81356120ab816119ec565b9392505050565b5f5f602083850312156120c3575f5ffd5b82356001600160401b038111156120d8575f5ffd5b6120e485828601611a10565b90969095509350505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f6120ab60208301846120f0565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b604081525f61216b604083018587612130565b905060018060a01b0383166020830152949350505050565b5f60208284031215612193575f5ffd5b815168ffffffffffffffffff811681146120ab575f5ffd5b634e487b7160e01b5f52601160045260245ffd5b81810381811115611658576116586121ab565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f81518060208401855e5f93019283525090919050565b5f61180461221f83866121fa565b846121fa565b5f6120ab82846121fa565b6001600160a01b03831681526040602080830182905283519183018290525f91908401906060840190835b8181101561227957835183526020938401939092019160010161225b565b50909695505050505050565b5f60208284031215612295575f5ffd5b815180151581146120ab575f5ffd5b80820180821115611658576116586121ab565b5f6122c282856121fa565b6101d160f51b81526122d760028201856121fa565b95945050505050565b5f5f5f5f608085870312156122f3575f5ffd5b845160048110612301575f5ffd5b602086015190945061231281611c3a565b604086015190935061232381611c48565b6060860151909250612334816119ec565b939692955090935050565b818382375f9101908152919050565b5f6020828403121561235e575f5ffd5b815162ffffff811681146120ab575f5ffd5b6001600160801b038181168382160290811690818114612392576123926121ab565b5092915050565b634e487b7160e01b5f52601260045260245ffd5b5f6001600160801b038316806123c5576123c5612399565b806001600160801b0384160491505092915050565b6001600160801b038281168282160390811115611658576116586121ab565b80516001600160a01b031682526020808201516001600160801b039081169184019190915260409182015116910152565b5f81516040845261243e60408501826120f0565b60208481015186830387830152805180845292935081019181840191600582901b8501015f5b828110156124e557601f19868303018452845160ff815116835260018060f81b03602082015116602084015260408101516040840152606081015160a060608501526124b360a08501826120f0565b90506080820151915083810360808501526124ce81836120f0565b602097880197969096019593505050600101612464565b50979650505050505050565b5f82825180855260208501945060208160051b830101602085015f5b8381101561227957848303601f19018852815180516003811061253e57634e487b7160e01b5f52602160045260245ffd5b84526020818101516affffffffffffffffffffff16908501526040808201516001600160a01b031690850152606090810151608091850182905290612585908501826120f0565b6020998a019990945092909201915060010161250d565b60c081525f6125ae60c08301876120f0565b6125bb60208401876123f9565b82810360808401526125cd818661242a565b905082810360a08401526125e181856124f1565b979650505050505050565b600181811c9082168061260057607f821691505b60208210810361261e57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561266b57805f5260205f20601f840160051c810160208510156126495750805b601f840160051c820191505b81811015612668575f8155600101612655565b50505b505050565b6001600160401b0383111561268757612687611a54565b61269b8361269583546125ec565b83612624565b5f601f8411600181146126cc575f85156126b55750838201355b5f19600387901b1c1916600186901b178355612668565b5f83815260208120601f198716915b828110156126fb57868501358255602094850194600190920191016126db565b5086821015612717575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b5f60208284031215612739575f5ffd5b5051919050565b60a081525f61275360a083018789612130565b856020840152828103806040850152600e82526d4563686f4d61726b65744461746160901b602083015285606085015260408101608085015250835160408201526020840151606082015260018060a01b036040850151166080820152606084015161012060a08301526127cb6101608301826120f0565b905060808501516127df60c08401826123f9565b5060a0850151828203603f19016101208401526127fc828261242a565b91505060c0850151603f198383030161014084015261281b82826124f1565b9a9950505050505050505050565b5f8261283757612837612399565b500490565b8082028115828204841417611658576116586121ab565b5f60018201612864576128646121ab565b5060010190565b602081525f611804602083018486612130565b5f606082840312801561288f575f5ffd5b50612898611a68565b82516001600160401b03811681146128ae575f5ffd5b815260208301516128be816119ec565b602082015260408301516128d1816119ec565b60408201529392505050565b5f602082840312156128ed575f5ffd5b81516120ab816119ec56fea264697066735822122031b2034a9c62e2c3b55a79565afdc450fb536d167a785e139895c3f80b32fed764736f6c634300081e0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000c64f8f0c754bc41a29f95946fa0abb3828c47e6d000000000000000000000000be852f3b38f65d5e251a3127c25dc21fa9a9129e
-----Decoded View---------------
Arg [0] : echoAdmin (address): 0xc64f8f0c754BC41A29F95946Fa0Abb3828C47e6d
Arg [1] : currentImplementation (address): 0xbe852F3b38f65d5e251a3127c25DC21Fa9A9129e
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000c64f8f0c754bc41a29f95946fa0abb3828c47e6d
Arg [1] : 000000000000000000000000be852f3b38f65d5e251a3127c25dc21fa9a9129e
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in FRAX
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.