Source Code
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 21425030 | 227 days ago | Contract Creation | 0 FRAX |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
EchoContents
Compiler Version
v0.8.30+commit.73712a01
Optimization Enabled:
Yes with 200 runs
Other Settings:
prague EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
// ███████╗ ██████╗██╗ ██╗ ██████╗ ███╗ ███╗ █████╗ ██████╗ ██╗ ██╗███████╗████████╗
// ██╔════╝██╔════╝██║ ██║██╔═══██╗ ████╗ ████║██╔══██╗██╔══██╗██║ ██╔╝██╔════╝╚══██╔══╝
// █████╗ ██║ ███████║██║ ██║ ██╔████╔██║███████║██████╔╝█████╔╝ █████╗ ██║
// ██╔══╝ ██║ ██╔══██║██║ ██║ ██║╚██╔╝██║██╔══██║██╔══██╗██╔═██╗ ██╔══╝ ██║
// ███████╗╚██████╗██║ ██║╚██████╔╝ ██║ ╚═╝ ██║██║ ██║██║ ██║██║ ██╗███████╗ ██║
// ╚══════╝ ╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚═╝
// ================================= EchoContents V1 =================================
// =================================== Spring 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 *-*-*-*-|
\*******************************/
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
);
}
/// @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;
totalQaKinds = _contentToCampaign[contentLink][campaign].totalQaKinds;
lastUpdatedTime = _contentToCampaign[contentLink][campaign]
.lastUpdatedTime;
if (bytes(actionKind).length != 0)
actionKindValue = _contentToCampaign[contentLink][campaign]
.actionKindToValue[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 (currentPage, totalPages, pagedArray);
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--;
}
}
}
}// 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;
/***************************\
|*-*-*-* 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 {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.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 memory 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
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/"
],
"evmVersion": "prague"
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"echoAdministration","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ACCESS_DENIED","type":"error"},{"inputs":[{"internalType":"uint256","name":"pct","type":"uint256"}],"name":"CHECK_QA_PCT","type":"error"},{"inputs":[{"internalType":"string","name":"contentLink","type":"string"}],"name":"CONTENT_DOESNT_EXIST","type":"error"},{"inputs":[],"name":"CONTENT_REGISTERED_FOR_CAMPAIGN_BEFORE","type":"error"},{"inputs":[],"name":"DIFFERENT_SOCIALS","type":"error"},{"inputs":[{"internalType":"string","name":"kpiKind","type":"string"}],"name":"GIVEN_KPI_KIND_DOESNT_EXIST_ON_CAMPAIGN","type":"error"},{"inputs":[{"internalType":"string","name":"qaKind","type":"string"}],"name":"GIVEN_QA_KIND_DOESNT_EXIST_ON_CAMPAIGN","type":"error"},{"inputs":[],"name":"ONLY_IN_PROGRESS_OR_PAUSED_OR_FINISHED_CAMPAIGN","type":"error"},{"inputs":[{"internalType":"string","name":"actionKind","type":"string"}],"name":"ORACLE_DOESNT_HAVE_REQUIRED_ACCESS","type":"error"},{"inputs":[],"name":"ORACLE_IS_BAN","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"name":"PRBMath_MulDiv18_Overflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"PRBMath_MulDiv_Overflow","type":"error"},{"inputs":[{"internalType":"UD60x18","name":"x","type":"uint256"}],"name":"PRBMath_UD60x18_Exp2_InputTooBig","type":"error"},{"inputs":[{"internalType":"UD60x18","name":"x","type":"uint256"}],"name":"PRBMath_UD60x18_Log_InputTooSmall","type":"error"},{"inputs":[{"internalType":"string","name":"contentLink","type":"string"}],"name":"QA_DATA_SETTLED_BEFORE","type":"error"},{"inputs":[],"name":"UNDEFINED_JOB","type":"error"},{"inputs":[],"name":"ZERO_CONTENT_QA_SCORE","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"campaign","type":"address"},{"indexed":true,"internalType":"uint256","name":"lastPostTime","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"campaignTotalPosts","type":"uint256"},{"indexed":false,"internalType":"string","name":"contentLink","type":"string"},{"indexed":false,"internalType":"uint256","name":"campaignMaxPerPost","type":"uint256"},{"indexed":false,"internalType":"string","name":"EchoMarketData","type":"string"},{"indexed":false,"internalType":"uint256","name":"totalPosts","type":"uint256"}],"name":"ContentApplied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"campaign","type":"address"},{"indexed":true,"internalType":"address","name":"oracle","type":"address"},{"indexed":false,"internalType":"string","name":"contentLink","type":"string"},{"components":[{"internalType":"string","name":"kind","type":"string"},{"internalType":"bytes","name":"value","type":"bytes"}],"indexed":false,"internalType":"struct ConfigOracleData","name":"newConfig","type":"tuple"}],"name":"ContentConfigAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"campaign","type":"address"},{"indexed":false,"internalType":"string","name":"contentLink","type":"string"},{"indexed":false,"internalType":"bool","name":"additionalMode","type":"bool"},{"indexed":true,"internalType":"uint256","name":"additionalContentEffectvieKPI","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"campaignTotalEffectiveKPIs","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"contentEffectiveKPI","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"campaignTotalEligibleContents","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lastUpdatedTime","type":"uint256"},{"indexed":false,"internalType":"string","name":"EchoMarketData","type":"string"},{"indexed":false,"internalType":"uint256","name":"totalEligibleContents","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalEffectiveKPIs","type":"uint256"}],"name":"ContentEffectiveKPIupdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"campaign","type":"address"},{"indexed":false,"internalType":"string","name":"contentLink","type":"string"},{"indexed":true,"internalType":"uint256","name":"QAoverallScore","type":"uint256"}],"name":"ContentQAoverallScore","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"campaign","type":"address"},{"indexed":false,"internalType":"string","name":"contentLink","type":"string"},{"indexed":true,"internalType":"uint256","name":"contentTotalKPI","type":"uint256"}],"name":"ContentTotalKPIupdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"campaign","type":"address"},{"indexed":true,"internalType":"address","name":"oracle","type":"address"},{"indexed":false,"internalType":"string","name":"contentLink","type":"string"},{"indexed":false,"internalType":"uint256","name":"lastCampaignUpdateTime","type":"uint256"},{"indexed":false,"internalType":"string","name":"socialKindKPI","type":"string"},{"indexed":false,"internalType":"uint256","name":"kpiAmount","type":"uint256"}],"name":"KPIupdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"campaign","type":"address"},{"indexed":true,"internalType":"address","name":"oracle","type":"address"},{"indexed":false,"internalType":"string","name":"contentLink","type":"string"},{"indexed":false,"internalType":"string","name":"qaMethodKind","type":"string"},{"indexed":false,"internalType":"uint256","name":"pctScore","type":"uint256"}],"name":"NewQualificationSettled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"campaign","type":"address"},{"indexed":false,"internalType":"string","name":"contentLink","type":"string"}],"name":"ReadyForKPIupdates","type":"event"},{"inputs":[],"name":"ECHO_ADMIN","outputs":[{"internalType":"contract IEchoAdministration","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ECHO_FACTORY","outputs":[{"internalType":"contract IEchoCampaignFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"contentLink","type":"string"},{"internalType":"uint256","name":"campaignMaxPerPost","type":"uint256"}],"name":"addContentLink","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"campaign","type":"address"}],"name":"campaignContentInfo","outputs":[{"internalType":"uint256","name":"lastPostTime","type":"uint256"},{"internalType":"uint256","name":"lastUpdatedTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"campaign","type":"address"},{"internalType":"string","name":"contentLink","type":"string"},{"internalType":"string","name":"actionKind","type":"string"}],"name":"contentToCampaign","outputs":[{"internalType":"bool","name":"contentRegistered","type":"bool"},{"internalType":"uint256","name":"totalQaKinds","type":"uint256"},{"internalType":"uint256","name":"lastUpdatedTime","type":"uint256"},{"internalType":"uint256","name":"actionKindValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"contents","outputs":[{"internalType":"address","name":"campaign","type":"address"},{"internalType":"string","name":"content","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"page","type":"uint256"},{"internalType":"address","name":"campaign","type":"address"}],"name":"paginatedCampaignContents","outputs":[{"internalType":"uint256","name":"currentPage","type":"uint256"},{"internalType":"uint256","name":"totalPages","type":"uint256"},{"internalType":"string[]","name":"pagedArray","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"page","type":"uint256"}],"name":"paginatedContentsWithCampaigns","outputs":[{"internalType":"uint256","name":"currentPage","type":"uint256"},{"internalType":"uint256","name":"totalPages","type":"uint256"},{"components":[{"internalType":"address","name":"campaign","type":"address"},{"internalType":"string","name":"content","type":"string"}],"internalType":"struct CampaignAndContent[]","name":"pagedArray","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"campaign","type":"address"},{"internalType":"string","name":"contentLink","type":"string"},{"components":[{"internalType":"string","name":"kind","type":"string"},{"internalType":"bytes","name":"value","type":"bytes"}],"internalType":"struct ConfigOracleData[]","name":"configs","type":"tuple[]"},{"components":[{"internalType":"enum QA_METHOD","name":"method","type":"uint8"},{"internalType":"uint256","name":"pct","type":"uint256"},{"internalType":"string","name":"kind","type":"string"}],"internalType":"struct QualificationOracleData[]","name":"qualifications","type":"tuple[]"}],"name":"setContentData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"campaign","type":"address"},{"internalType":"string","name":"contentLink","type":"string"},{"components":[{"internalType":"string","name":"social","type":"string"},{"components":[{"internalType":"string","name":"method","type":"string"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct KPIsOracleData[]","name":"kpis","type":"tuple[]"}],"internalType":"struct SocialKPIsOracleData","name":"socialKPIs","type":"tuple"}],"name":"updateKPIs","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60e06040525f60c052348015610013575f5ffd5b50604051614c86380380614c8683398101604081905261003291610047565b6001600160a01b03166080523360a052610074565b5f60208284031215610057575f5ffd5b81516001600160a01b038116811461006d575f5ffd5b9392505050565b60805160a05160c051614b716101155f395f8181611ea101528181611f9a0152818161287601528181612a3b01528181612a6601528181612a9601528181612ac001528181612b3201528181612b5d0152612bbe01525f818161013101526106c601525f81816101fb015281816110dc0152818161117f015281816112da015281816117f201528181612019015281816120bc01526124fa0152614b715ff3fe608060405234801561000f575f5ffd5b506004361061009b575f3560e01c8063aa8ff04e11610063578063aa8ff04e1461018d578063b5ecf912146101c2578063b6967b3b146101e3578063cfce1a99146101f6578063efdbb6311461021d575f5ffd5b806310cf02b91461009f578063431facbc146100f5578063603ab8b71461011757806395953ee11461012c5780639bff4ada1461016b575b5f5ffd5b6100db6100ad366004613970565b6001600160a01b03165f908152600560205260409020546001600160801b03600160801b8204811692911690565b604080519283526020830191909152015b60405180910390f35b61010861010336600461398b565b610230565b6040516100ec939291906139d0565b61012a610125366004613aa5565b6106ba565b005b6101537f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100ec565b61017e610179366004613aec565b6109c8565b6040516100ec93929190613b1a565b6101a061019b366004613b7d565b610ec5565b60408051941515855260208501939093529183015260608201526080016100ec565b6101d56101d036600461398b565b610ff1565b6040516100ec929190613bfd565b61012a6101f1366004613c60565b6110af565b6101537f000000000000000000000000000000000000000000000000000000000000000081565b61012a61022b366004613d0e565b612016565b6003545f908190606090156106b357600354600b11156103df576003546001600160401b0381111561026457610264613d8c565b6040519080825280602002602001820160405280156102a957816020015b604080518082019091525f8152606060208201528152602001906001900390816102825790505b5090505f5b6003805482906102c090600190613db4565b6102ca9190613db4565b815481106102da576102da613dc7565b5f9182526020918290206040805180820190915260029092020180546001600160a01b03168252600181018054929391929184019161031890613ddb565b80601f016020809104026020016040519081016040528092919081815260200182805461034490613ddb565b801561038f5780601f106103665761010080835404028352916020019161038f565b820191905f5260205f20905b81548152906001019060200180831161037257829003601f168201915b5050505050815250508282815181106103aa576103aa613dc7565b60209081029190910101526003546103c490600190613db4565b81146103d2576001016102ae565b60018093509350506106b3565b835f036103eb57600193505b6003546103fa90600a90613e27565b91505f61040883600a613e46565b6003546104159190613db4565b60035490915061042684600a613e46565b101561043a578261043681613e5d565b9350505b82851115610446578294505b8493505f5f866001036104765760035461046290600190613db4565b915061046f600a83613db4565b9050610533565b8487036104a05782156104935761048e600184613db4565b610499565b60099150815b9150610533565b825f036104ad575f6104b8565b6104b8600184613db4565b6104c28887613db4565b6104cd90600a613e46565b6104d79190613e75565b6104e19083613e75565b9150825f036104f0575f6104fb565b6104fb600184613db4565b60016105078988613db4565b6105119190613db4565b61051c90600a613e46565b6105269190613e75565b6105309082613e75565b90505b8061053f836001613e75565b6105499190613db4565b6001600160401b0381111561056057610560613d8c565b6040519080825280602002602001820160405280156105a557816020015b604080518082019091525f81526060602082015281526020019060019003908161057e5790505b5093505f5b600383815481106105bd576105bd613dc7565b5f9182526020918290206040805180820190915260029092020180546001600160a01b0316825260018101805492939192918401916105fb90613ddb565b80601f016020809104026020016040519081016040528092919081815260200182805461062790613ddb565b80156106725780601f1061064957610100808354040283529160200191610672565b820191905f5260205f20905b81548152906001019060200180831161065557829003601f168201915b50505050508152505085828151811061068d5761068d613dc7565b6020026020010181905250818303156106ae575f19909201916001016105aa565b505050505b9193909250565b5f6001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632c960900336001600160a01b03166306fdde036040518163ffffffff1660e01b81526004015f60405180830381865afa15801561072e573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526107559190810190613f8f565b6040518263ffffffff1660e01b81526004016107719190613fc0565b606060405180830381865afa15801561078c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107b09190613fd2565b602001516001600160a01b0316036107db576040516395383ea160e01b815260040160405180910390fd5b600483836040516107ed92919061404e565b9081526040805160209281900383019020335f908152925290205460ff1615610829576040516321947e9960e21b815260040160405180910390fd5b60036040518060400160405280336001600160a01b0316815260200185858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920182905250939094525050835460018082018655948252602091829020845160029092020180546001600160a01b0319166001600160a01b03909216919091178155908301519293909290830191506108ca90826140a9565b5050506001600484846040516108e192919061404e565b9081526040805160209281900383019020335f908152908352818120805460ff191694151594909417909355600582528220600190810180549182018155835291200161092f838583614163565b50335f90815260056020526040812080546001600160801b03428116600160801b029116179055600180549161096483613e5d565b9091555050335f8181526005602052604090819020600190810154905491519092429290917fe50964cbc470160e6c3556ba4ea3ba3e84778d99446d737c1d1a8a4c9c50eb03916109bb9189918991899190614244565b60405180910390a4505050565b6001600160a01b0381165f90815260056020526040812060010154819060609015610ebe576001600160a01b0384165f90815260056020526040902060010154600b1115610bb2576001600160a01b0384165f908152600560205260409020600101546001600160401b03811115610a4257610a42613d8c565b604051908082528060200260200182016040528015610a7557816020015b6060815260200190600190039081610a605790505b5090505f5b6001600160a01b0385165f9081526005602052604090206001908101805490918391610aa69190613db4565b610ab09190613db4565b81548110610ac057610ac0613dc7565b905f5260205f20018054610ad390613ddb565b80601f0160208091040260200160405190810160405280929190818152602001828054610aff90613ddb565b8015610b4a5780601f10610b2157610100808354040283529160200191610b4a565b820191905f5260205f20905b815481529060010190602001808311610b2d57829003601f168201915b5050505050828281518110610b6157610b61613dc7565b6020908102919091018101919091526001600160a01b0386165f908152600590915260409020600190810154610b979190613db4565b8114610ba557600101610a7a565b6001809350935050610ebe565b845f03610bbe57600194505b6001600160a01b0384165f90815260056020526040902060010154610be590600a90613e27565b91505f610bf383600a613e46565b6001600160a01b0386165f90815260056020526040902060010154610c189190613db4565b6001600160a01b0386165f90815260056020526040902060010154909150610c4184600a613e46565b1015610c555782610c5181613e5d565b9350505b82861115610c61578295505b8593505f5f87600103610ca9576001600160a01b0387165f908152600560205260409020600190810154610c959190613db4565b9150610ca2600a83613db4565b9050610d66565b848803610cd3578215610cc657610cc1600184613db4565b610ccc565b60099150815b9150610d66565b825f03610ce0575f610ceb565b610ceb600184613db4565b610cf58987613db4565b610d0090600a613e46565b610d0a9190613e75565b610d149083613e75565b9150825f03610d23575f610d2e565b610d2e600184613db4565b6001610d3a8a88613db4565b610d449190613db4565b610d4f90600a613e46565b610d599190613e75565b610d639082613e75565b90505b80610d72836001613e75565b610d7c9190613db4565b6001600160401b03811115610d9357610d93613d8c565b604051908082528060200260200182016040528015610dc657816020015b6060815260200190600190039081610db15790505b5093505f5b6001600160a01b0388165f908152600560205260409020600101805484908110610df757610df7613dc7565b905f5260205f20018054610e0a90613ddb565b80601f0160208091040260200160405190810160405280929190818152602001828054610e3690613ddb565b8015610e815780601f10610e5857610100808354040283529160200191610e81565b820191905f5260205f20905b815481529060010190602001808311610e6457829003601f168201915b5050505050858281518110610e9857610e98613dc7565b602002602001018190525081830315610eb9575f1990920191600101610dcb565b505050505b9250925092565b5f5f5f5f60048888604051610edb92919061404e565b9081526040805191829003602090810183206001600160a01b038d165f9081529152205460ff169450600490610f14908a908a9061404e565b90815260408051602092819003830181206001600160a01b038d165f9081529352912054610100900460ff169350600490610f52908a908a9061404e565b90815260408051602092819003830190206001600160a01b038c165f90815292529020546201000090046001600160f01b031691508415610fe55760048888604051610f9f92919061404e565b9081526040805191829003602090810183206001600160a01b038d165f90815291522060020190610fd3908890889061404e565b90815260200160405180910390205490505b95509550955095915050565b60038181548110611000575f80fd5b5f918252602090912060029091020180546001820180546001600160a01b0390921693509061102e90613ddb565b80601f016020809104026020016040519081016040528092919081815260200182805461105a90613ddb565b80156110a55780601f1061107c576101008083540402835291602001916110a5565b820191905f5260205f20905b81548152906001019060200180831161108857829003601f168201915b5050505050905082565b821580156110bb575080155b156110d957604051635b9e1d4560e11b815260040160405180910390fd5b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa158015611136573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061115a919061429c565b604051632ac1326b60e01b815233600482018190526001600160a01b039283161492507f000000000000000000000000000000000000000000000000000000000000000090911690632ac1326b90602401602060405180830381865afa1580156111c6573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111ea91906142b7565b80156111f4575080155b1561121257604051631e4230c760e21b815260040160405180910390fd5b6004878760405161122492919061404e565b90815260408051602092819003830190206001600160a01b038b165f908152925290205460ff16611275578686604051633c00a7af60e01b815260040161126c9291906142d6565b60405180910390fd5b606084156114f1575f5f5b868110156114ee5787878281811061129a5761129a613dc7565b90506020028101906112ac91906142e9565b6112b69080614307565b6040516020016112c7929190614349565b60405160208183030381529060405292507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b6001e2460018560405160200161131a9190614383565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b81526004016113619291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa15801561137c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113a091906143b9565b94505050506001600160a01b03821633148015906113bc575083155b156113df578260405160016218620560e01b0319815260040161126c9190613fc0565b8787828181106113f1576113f1613dc7565b905060200281019061140391906142e9565b611411906020810190614307565b61141a91614406565b60405160049061142d908d908d9061404e565b90815260200160405180910390205f8d6001600160a01b03166001600160a01b031681526020019081526020015f206002018460405161146d9190614383565b90815260405190819003602001902055336001600160a01b038c167fd7b65b61e10654aaf9ab3a7af2b11e30c5c7e3b2ce4dfe8378f8ca14687c2d1e8c8c8c8c878181106114bd576114bd613dc7565b90506020028101906114cf91906142e9565b6040516114de93929190614464565b60405180910390a3600101611280565b50505b821561200b575f896001600160a01b03166326f016376040518163ffffffff1660e01b81526004015f60405180830381865afa158015611533573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261155a91908101906144f9565b5190508484905060048a8a60405161157392919061404e565b90815260408051602092819003830190206001600160a01b038e165f90815292529020546115a990610100900460ff1683613db4565b141580156115b5575082155b156115d7578888604051635170461160e11b815260040161126c9291906142d6565b5f5f5b85811015611ba1575f8787838181106115f5576115f5613dc7565b90506020028101906116079190614641565b611615906020810190614655565b60028111156116265761162661462d565b036116845786868281811061163d5761163d613dc7565b905060200281019061164f9190614641565b61165d906040810190614307565b60405160200161166e929190614670565b6040516020818303038152906040529350611766565b600187878381811061169857611698613dc7565b90506020028101906116aa9190614641565b6116b8906020810190614655565b60028111156116c9576116c961462d565b03611711578686828181106116e0576116e0613dc7565b90506020028101906116f29190614641565b611700906040810190614307565b60405160200161166e929190614692565b86868281811061172357611723613dc7565b90506020028101906117359190614641565b611743906040810190614307565b6040516020016117549291906146b3565b60405160208183030381529060405293505b60405163aa588b4b60e01b81526001600160a01b038d169063aa588b4b90611792908790600401613fc0565b602060405180830381865afa1580156117ad573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117d191906142b7565b6117f05783604051633cfbd52760e21b815260040161126c9190613fc0565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b6001e2433866040516020016118319190614383565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b81526004016118789291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa158015611893573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118b791906143b9565b50919350600291506118c69050565b8260038111156118d8576118d861462d565b141580156118e4575084155b15611907578360405160016218620560e01b0319815260040161126c9190613fc0565b620186a087878381811061191d5761191d613dc7565b905060200281019061192f9190614641565b602001351180611964575086868281811061194c5761194c613dc7565b905060200281019061195e9190614641565b60200135155b156119ad5786868281811061197b5761197b613dc7565b905060200281019061198d9190614641565b602001356040516370e00d7760e01b815260040161126c91815260200190565b60048b8b6040516119bf92919061404e565b90815260200160405180910390205f8d6001600160a01b03166001600160a01b031681526020019081526020015f20600201846040516119ff9190614383565b9081526020016040518091039020545f14611a3b5784611a36578a8a604051635170461160e11b815260040161126c9291906142d6565b611aa0565b60048b8b604051611a4d92919061404e565b90815260408051602092819003830190206001600160a01b038f165f908152925290208054610100900460ff16906001611a86836146d4565b91906101000a81548160ff021916908360ff160217905550505b868682818110611ab257611ab2613dc7565b9050602002810190611ac49190614641565b6020013560048c8c604051611ada92919061404e565b90815260200160405180910390205f8e6001600160a01b03166001600160a01b031681526020019081526020015f2060020185604051611b1a9190614383565b90815260405190819003602001902055336001600160a01b038d167fb72e5373934391ba6c4fc5b9542ee21c20a5d890a85cfdd16d446357e83af2888d8d888c8c88818110611b6b57611b6b613dc7565b9050602002810190611b7d9190614641565b60200135604051611b9194939291906146f2565b60405180910390a36001016115da565b8260048c8c604051611bb492919061404e565b90815260200160405180910390205f8e6001600160a01b03166001600160a01b031681526020019081526020015f205f0160019054906101000a900460ff1660ff1603611f50575f670de0b6b3a764000090505f8d6001600160a01b03166326f016376040518163ffffffff1660e01b81526004015f60405180830381865afa158015611c43573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611c6a91908101906144f9565b90505f8d8d8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f81840152601f19601f8201169050808301925050505050505090505f8f90505f94505b8251851015611ed1575f838681518110611cd757611cd7613dc7565b60200260200101515f01516002811115611cf357611cf361462d565b03611d3c57828581518110611d0a57611d0a613dc7565b602002602001015160600151604051602001611d269190614729565b6040516020818303038152906040529750611ddf565b6001838681518110611d5057611d50613dc7565b60200260200101515f01516002811115611d6c57611d6c61462d565b03611d9f57828581518110611d8357611d83613dc7565b602002602001015160600151604051602001611d269190614744565b828581518110611db157611db1613dc7565b602002602001015160600151604051602001611dcd919061475e565b60405160208183030381529060405297505b611e9d611e96611e28858881518110611dfa57611dfa613dc7565b602002602001015160200151662386f26fc10000611e189190614778565b6affffffffffffffffffffff1690565b611e90600486604051611e3b9190614383565b9081526040805191829003602090810183206001600160a01b0389165f90815291522060020190611e6d908e90614383565b9081526020016040518091039020546509184e72a000611e8d9190613e46565b90565b90612da0565b8590612eae565b93507f00000000000000000000000000000000000000000000000000000000000000008414611ed157600190940193611cbb565b83600483604051611ee29190614383565b90815260408051602092819003830190206001600160a01b0385165f908152925290206001015583816001600160a01b03167ffd61a2d01d4536539eb3388a9803c27ef3a5b28bb51773f83534e77bf02770d784604051611f439190613fc0565b60405180910390a3505050505b611fbe60048c8c604051611f6592919061404e565b90815260200160405180910390205f8e6001600160a01b03166001600160a01b031681526020019081526020015f20600101547f0000000000000000000000000000000000000000000000000000000000000000141590565b15612007578b6001600160a01b03167ffb227420d8844fd0693b8044d6444d0d36e18e02034526f9d379c05612e719838c8c604051611ffe9291906142d6565b60405180910390a25b5050505b505050505050505050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa158015612073573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612097919061429c565b604051632ac1326b60e01b815233600482018190526001600160a01b039283161492507f000000000000000000000000000000000000000000000000000000000000000090911690632ac1326b90602401602060405180830381865afa158015612103573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061212791906142b7565b8015612131575080155b1561214f57604051631e4230c760e21b815260040160405180910390fd5b6004848460405161216192919061404e565b90815260408051602092819003830190206001600160a01b0388165f908152925290205460ff166121a9578383604051633c00a7af60e01b815260040161126c9291906142d6565b5f856001600160a01b031663ef8a92356040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121e6573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061220a91906147a5565b905060018160058111156122205761222061462d565b141580156122405750600281600581111561223d5761223d61462d565b14155b801561225e5750600381600581111561225b5761225b61462d565b14155b8015612268575081155b156122865760405163700f030f60e01b815260040160405180910390fd5b6122c56004868660405161229b92919061404e565b90815260408051602092819003830190206001600160a01b038a165f908152925290206001015490565b5f036122e4576040516306b2e62960e11b815260040160405180910390fd5b5f866001600160a01b0316638c7a8eb36040518163ffffffff1660e01b81526004015f60405180830381865afa158015612320573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261234791908101906147c3565b90505f6123548580614307565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505060405192935061239c928492506020019050614383565b60408051601f19818403018152908290528051602091820120845190926123c39201614383565b60405160208183030381529060405280519060200120146123f757604051637128647b60e11b815260040160405180910390fd5b5f60605f5b6124096020890189614962565b9050831015612728578361242060208a018a614962565b8581811061243057612430613dc7565b905060200281019061244291906142e9565b61244c9080614307565b60405160200161245e939291906149a7565b60408051601f198184030181529082905263aa588b4b60e01b825292506001600160a01b038c169063aa588b4b9061249a908590600401613fc0565b602060405180830381865afa1580156124b5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124d991906142b7565b6124f85781604051631f3f577f60e21b815260040161126c9190613fc0565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b6001e245f846040516020016125399190614383565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b81526004016125809291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa15801561259b573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125bf91906143b9565b93505050336001600160a01b03831614801591506125db575086155b156125fe578160405160016218620560e01b0319815260040161126c9190613fc0565b61260b6020890189614962565b8481811061261b5761261b613dc7565b905060200281019061262d91906142e9565b6020013560048b8b60405161264392919061404e565b90815260200160405180910390205f8d6001600160a01b03166001600160a01b031681526020019081526020015f20600201836040516126839190614383565b908152602001604051809103902081905550336001600160a01b03168b6001600160a01b03167f9e9ec73da222bb34e89c444ee2841702e1509018ad901b4deb68b0ef9b859f398c8c42878e80602001906126de9190614962565b8b8181106126ee576126ee613dc7565b905060200281019061270091906142e9565b602001356040516127159594939291906149d3565b60405180910390a36001909201916123fc565b6001600160a01b038b165f90815260056020526040812080546fffffffffffffffffffffffffffffffff1916426001600160801b03161790559250825f8b8b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152509293508f925050505b87602001515186101561297d5786886020015187815181106127c4576127c4613dc7565b6020026020010151606001516040516020016127e1929190614a11565b60405160208183030381529060405294506004826040516128029190614383565b9081526040805191829003602090810183206001600160a01b0385165f90815291522060020190612834908790614383565b9081526020016040518091039020548860200151878151811061285957612859613dc7565b6020026020010151602001516001600160f81b0316111561289c577f0000000000000000000000000000000000000000000000000000000000000000925061297d565b61297061296961292a6128cd8b602001518a815181106128be576128be613dc7565b60200260200101516040015190565b6129246004876040516128e09190614383565b9081526040805191829003602090810183206001600160a01b038a165f90815291522060020190612912908c90614383565b90815260200160405180910390205490565b90612ec3565b6129638b602001518a8151811061294357612943613dc7565b60200260200101515f015160ff16662386f26fc10000611e8d9190613e46565b90612eae565b8490612eda565b60019096019592506127a0565b82816001600160a01b03167fc8d7d7296c924c3920078949d915bb20c80351022f3d90dda892192ad65cd974846040516129b79190613fc0565b60405180910390a35f5f5f612a30846001600160a01b03166324034a0e876040518263ffffffff1660e01b81526004016129f19190613fc0565b602060405180830381865afa158015612a0c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e8d9190614a3a565b92505f9150600190507f00000000000000000000000000000000000000000000000000000000000000008314158015612a8857507f000000000000000000000000000000000000000000000000000000000000000086145b15612a9457505f612bbc565b7f000000000000000000000000000000000000000000000000000000000000000083148015612ae357507f00000000000000000000000000000000000000000000000000000000000000008614155b15612b3057612b29600486604051612afb9190614383565b90815260408051602092819003830190206001600160a01b0388165f90815292529020600101548790612eae565b9150612bbc565b7f00000000000000000000000000000000000000000000000000000000000000008314158015612b8057507f00000000000000000000000000000000000000000000000000000000000000008614155b15612bbc57612b98600486604051612afb9190614383565b915081831015612bac57612b298284612ee8565b612bb68383612ee8565b91505f90505b7f00000000000000000000000000000000000000000000000000000000000000008214612d8d575f8080806001600160a01b03881663a7c64c708a88886040518463ffffffff1660e01b8152600401612c1793929190614a51565b6080604051808303815f875af1158015612c33573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c579190614a7a565b604051939750919550935091504290600490612c74908c90614383565b90815260408051602092819003830190206001600160a01b038c165f90815292528120805461ffff16620100006001600160f01b0394909416939093029290921790915581900b15612cf857805f0b600114612ce25760028054905f612cd983614ab4565b91905055612cf6565b60028054905f612cf183613e5d565b919050555b505b84612d1957855f80548190612d0e908490613db4565b925050819055612d31565b855f80548190612d2a908490613e75565b9250508190555b508286896001600160a01b03167fc75368439f060eb528fcbcf2dcce8dbca5f2e6a36d590b59a7d354d4c6ce96f28c898988425f600201545f5f0154604051612d809796959493929190614ac9565b60405180910390a4505050505b5050505050505050505050505050505050565b5f8282818303612dc9578015612db6575f612dc0565b670de0b6b3a76400005b92505050612ea8565b670de0b6b3a76400008203612dea57670de0b6b3a764000092505050612ea8565b805f03612e0357670de0b6b3a764000092505050612ea8565b670de0b6b3a76400008103612e1c578492505050612ea8565b670de0b6b3a7640000821115612e4d57612e46612e41612e3b87612ef6565b86612eae565b61301d565b9250612ea5565b5f612e6a611e8d846ec097ce7bc90715b34b9f1000000000613e27565b90505f612e82612e41612e7c84612ef6565b88612eae565b9050612ea0611e8d826ec097ce7bc90715b34b9f1000000000613e27565b945050505b50505b92915050565b5f612ebc611e8d8484613079565b9392505050565b5f612ebc611e8d84670de0b6b3a76400008561312b565b5f612ebc611e8d8385613e75565b5f612ebc611e8d8385613db4565b5f81670de0b6b3a7640000811015612f245760405163036d32ef60e41b81526004810184905260240161126c565b5f612fa5670de0b6b3a7640000830460016001600160801b03821160071b91821c6001600160401b03811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c60ff8111600390811b91821c600f811160021b90811c918211871b91821c969096119490961792909217171791909117919091171790565b9050670de0b6b3a7640000810282821c670de0b6b3a763ffff198101612fce5750949350505050565b671bc16d674ec800006706f05b59d3b200005b801561301157670de0b6b3a7640000838002049250818310613009579283019260019290921c915b60011c612fe1565b50919695505050505050565b5f81680a688906bd8affffff81111561304c5760405163b3b6ba1f60e01b81526004810184905260240161126c565b5f613063670de0b6b3a7640000604084901b613e27565b9050613071611e8d826131f9565b949350505050565b5f80805f19848609848602925082811083820303915050805f036130aa5750670de0b6b3a764000090049050612ea8565b670de0b6b3a764000081106130dc57604051635173648d60e01b8152600481018690526024810185905260440161126c565b5f670de0b6b3a764000085870962040000818503049310909103600160ee1b02919091177faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106690291505092915050565b5f80805f19858709858702925082811083820303915050805f036131625783828161315857613158613e13565b0492505050612ebc565b83811061319357604051630c740aef60e31b815260048101879052602481018690526044810185905260640161126c565b5f84868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203025f889003889004909101858311909403939093029303949094049190911702949350505050565b600160bf1b67ff000000000000008216156133065767800000000000000082161561322d5768016a09e667f3bcc9090260401c5b67400000000000000082161561324c576801306fe0a31b7152df0260401c5b67200000000000000082161561326b576801172b83c7d517adce0260401c5b67100000000000000082161561328a5768010b5586cf9890f62a0260401c5b6708000000000000008216156132a9576801059b0d31585743ae0260401c5b6704000000000000008216156132c857680102c9a3e778060ee70260401c5b6702000000000000008216156132e75768010163da9fb33356d80260401c5b67010000000000000082161561330657680100b1afa5abcbed610260401c5b66ff0000000000008216156134055766800000000000008216156133335768010058c86da1c09ea20260401c5b6640000000000000821615613351576801002c605e2e8cec500260401c5b662000000000000082161561336f57680100162f3904051fa10260401c5b661000000000000082161561338d576801000b175effdc76ba0260401c5b66080000000000008216156133ab57680100058ba01fb9f96d0260401c5b66040000000000008216156133c95768010002c5cc37da94920260401c5b66020000000000008216156133e7576801000162e525ee05470260401c5b66010000000000008216156134055768010000b17255775c040260401c5b65ff00000000008216156134fb5765800000000000821615613430576801000058b91b5bc9ae0260401c5b6540000000000082161561344d57680100002c5c89d5ec6d0260401c5b6520000000000082161561346a5768010000162e43f4f8310260401c5b6510000000000082161561348757680100000b1721bcfc9a0260401c5b650800000000008216156134a45768010000058b90cf1e6e0260401c5b650400000000008216156134c1576801000002c5c863b73f0260401c5b650200000000008216156134de57680100000162e430e5a20260401c5b650100000000008216156134fb576801000000b1721835510260401c5b64ff000000008216156135e85764800000000082161561352457680100000058b90c0b490260401c5b6440000000008216156135405768010000002c5c8601cc0260401c5b64200000000082161561355c576801000000162e42fff00260401c5b6410000000008216156135785768010000000b17217fbb0260401c5b640800000000821615613594576801000000058b90bfce0260401c5b6404000000008216156135b057680100000002c5c85fe30260401c5b6402000000008216156135cc5768010000000162e42ff10260401c5b6401000000008216156135e857680100000000b17217f80260401c5b63ff0000008216156136cc57638000000082161561360f5768010000000058b90bfc0260401c5b634000000082161561362a576801000000002c5c85fe0260401c5b632000000082161561364557680100000000162e42ff0260401c5b6310000000821615613660576801000000000b17217f0260401c5b630800000082161561367b57680100000000058b90c00260401c5b63040000008216156136965768010000000002c5c8600260401c5b63020000008216156136b1576801000000000162e4300260401c5b63010000008216156136cc5768010000000000b172180260401c5b62ff00008216156137a757628000008216156136f1576801000000000058b90c0260401c5b6240000082161561370b57680100000000002c5c860260401c5b622000008216156137255768010000000000162e430260401c5b6210000082161561373f57680100000000000b17210260401c5b620800008216156137595768010000000000058b910260401c5b62040000821615613773576801000000000002c5c80260401c5b6202000082161561378d57680100000000000162e40260401c5b620100008216156137a7576801000000000000b1720260401c5b61ff00821615613879576180008216156137ca57680100000000000058b90260401c5b6140008216156137e35768010000000000002c5d0260401c5b6120008216156137fc576801000000000000162e0260401c5b6110008216156138155768010000000000000b170260401c5b61080082161561382e576801000000000000058c0260401c5b61040082161561384757680100000000000002c60260401c5b61020082161561386057680100000000000001630260401c5b61010082161561387957680100000000000000b10260401c5b60ff82161561394257608082161561389a57680100000000000000590260401c5b60408216156138b2576801000000000000002c0260401c5b60208216156138ca57680100000000000000160260401c5b60108216156138e2576801000000000000000b0260401c5b60088216156138fa57680100000000000000060260401c5b600482161561391257680100000000000000030260401c5b600282161561392a57680100000000000000010260401c5b600182161561394257680100000000000000010260401c5b670de0b6b3a76400000260409190911c60bf031c90565b6001600160a01b038116811461396d575f5ffd5b50565b5f60208284031215613980575f5ffd5b8135612ebc81613959565b5f6020828403121561399b575f5ffd5b5035919050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b5f606082018583528460208401526060604084015280845180835260808501915060808160051b8601019250602086015f5b82811015613a5357868503607f19018452815180516001600160a01b03168652602090810151604091870182905290613a3d908701826139a2565b9550506020938401939190910190600101613a02565b509298975050505050505050565b5f5f83601f840112613a71575f5ffd5b5081356001600160401b03811115613a87575f5ffd5b602083019150836020828501011115613a9e575f5ffd5b9250929050565b5f5f5f60408486031215613ab7575f5ffd5b83356001600160401b03811115613acc575f5ffd5b613ad886828701613a61565b909790965060209590950135949350505050565b5f5f60408385031215613afd575f5ffd5b823591506020830135613b0f81613959565b809150509250929050565b5f606082018583528460208401526060604084015280845180835260808501915060808160051b8601019250602086015f5b82811015613a5357607f19878603018452613b688583516139a2565b94506020938401939190910190600101613b4c565b5f5f5f5f5f60608688031215613b91575f5ffd5b8535613b9c81613959565b945060208601356001600160401b03811115613bb6575f5ffd5b613bc288828901613a61565b90955093505060408601356001600160401b03811115613be0575f5ffd5b613bec88828901613a61565b969995985093965092949392505050565b6001600160a01b03831681526040602082018190525f90613071908301846139a2565b5f5f83601f840112613c30575f5ffd5b5081356001600160401b03811115613c46575f5ffd5b6020830191508360208260051b8501011115613a9e575f5ffd5b5f5f5f5f5f5f5f6080888a031215613c76575f5ffd5b8735613c8181613959565b965060208801356001600160401b03811115613c9b575f5ffd5b613ca78a828b01613a61565b90975095505060408801356001600160401b03811115613cc5575f5ffd5b613cd18a828b01613c20565b90955093505060608801356001600160401b03811115613cef575f5ffd5b613cfb8a828b01613c20565b989b979a50959850939692959293505050565b5f5f5f5f60608587031215613d21575f5ffd5b8435613d2c81613959565b935060208501356001600160401b03811115613d46575f5ffd5b613d5287828801613a61565b90945092505060408501356001600160401b03811115613d70575f5ffd5b850160408188031215613d81575f5ffd5b939692955090935050565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b81810381811115612ea857612ea8613da0565b634e487b7160e01b5f52603260045260245ffd5b600181811c90821680613def57607f821691505b602082108103613e0d57634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52601260045260245ffd5b5f82613e4157634e487b7160e01b5f52601260045260245ffd5b500490565b8082028115828204841417612ea857612ea8613da0565b5f60018201613e6e57613e6e613da0565b5060010190565b80820180821115612ea857612ea8613da0565b604051608081016001600160401b0381118282101715613eaa57613eaa613d8c565b60405290565b604080519081016001600160401b0381118282101715613eaa57613eaa613d8c565b60405160a081016001600160401b0381118282101715613eaa57613eaa613d8c565b604051601f8201601f191681016001600160401b0381118282101715613f1c57613f1c613d8c565b604052919050565b5f82601f830112613f33575f5ffd5b81516001600160401b03811115613f4c57613f4c613d8c565b613f5f601f8201601f1916602001613ef4565b818152846020838601011115613f73575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f60208284031215613f9f575f5ffd5b81516001600160401b03811115613fb4575f5ffd5b61307184828501613f24565b602081525f612ebc60208301846139a2565b5f6060828403128015613fe3575f5ffd5b50604051606081016001600160401b038111828210171561400657614006613d8c565b60405282516001600160401b038116811461401f575f5ffd5b8152602083015161402f81613959565b6020820152604083015161404281613959565b60408201529392505050565b818382375f9101908152919050565b601f8211156140a457805f5260205f20601f840160051c810160208510156140825750805b601f840160051c820191505b818110156140a1575f815560010161408e565b50505b505050565b81516001600160401b038111156140c2576140c2613d8c565b6140d6816140d08454613ddb565b8461405d565b6020601f821160018114614108575f83156140f15750848201515b5f19600385901b1c1916600184901b1784556140a1565b5f84815260208120601f198516915b828110156141375787850151825560209485019460019092019101614117565b508482101561415457868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6001600160401b0383111561417a5761417a613d8c565b61418e836141888354613ddb565b8361405d565b5f601f8411600181146141bf575f85156141a85750838201355b5f19600387901b1c1916600186901b1783556140a1565b5f83815260208120601f198716915b828110156141ee57868501358255602094850194600190920191016141ce565b508682101561420a575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f61425760808301868861421c565b846020840152828103604084015261428a81600e81526d4563686f4d61726b65744461746160901b602082015260400190565b91505082606083015295945050505050565b5f602082840312156142ac575f5ffd5b8151612ebc81613959565b5f602082840312156142c7575f5ffd5b81518015158114612ebc575f5ffd5b602081525f61307160208301848661421c565b5f8235603e198336030181126142fd575f5ffd5b9190910192915050565b5f5f8335601e1984360301811261431c575f5ffd5b8301803591506001600160401b03821115614335575f5ffd5b602001915036819003821315613a9e575f5ffd5b67021a7a72324a39d160c51b8152818360088301375f9101600801908152919050565b5f81518060208401855e5f93019283525090919050565b5f612ebc828461436c565b805160ff8116811461439e575f5ffd5b919050565b80516001600160f81b038116811461439e575f5ffd5b5f5f5f5f608085870312156143cc575f5ffd5b8451600481106143da575f5ffd5b93506143e86020860161438e565b92506143f6604086016143a3565b91506060850151613d8181613959565b80356020831015612ea8575f19602084900360031b1b1692915050565b5f5f8335601e19843603018112614438575f5ffd5b83016020810192503590506001600160401b03811115614456575f5ffd5b803603821315613a9e575f5ffd5b604081525f61447760408301858761421c565b82810360208401526144898485614423565b6040835261449b60408401828461421c565b9150506144ab6020860186614423565b83830360208501526144be83828461421c565b9998505050505050505050565b5f6001600160401b038211156144e3576144e3613d8c565b5060051b60200190565b6003811061396d575f5ffd5b5f60208284031215614509575f5ffd5b81516001600160401b0381111561451e575f5ffd5b8201601f8101841361452e575f5ffd5b805161454161453c826144cb565b613ef4565b8082825260208201915060208360051b850101925086831115614562575f5ffd5b602084015b838110156146225780516001600160401b03811115614584575f5ffd5b85016080818a03601f19011215614599575f5ffd5b6145a1613e88565b60208201516145af816144ed565b815260408201516affffffffffffffffffffff811681146145ce575f5ffd5b602082015260608201516145e181613959565b604082015260808201516001600160401b038111156145fe575f5ffd5b61460d8b602083860101613f24565b60608301525084525060209283019201614567565b509695505050505050565b634e487b7160e01b5f52602160045260245ffd5b5f8235605e198336030181126142fd575f5ffd5b5f60208284031215614665575f5ffd5b8135612ebc816144ed565b66028a096a0a49d160cd1b8152818360078301375f9101600701908152919050565b65028a096a19d160d51b8152818360068301375f9101600601908152919050565b65028a096ab1d160d51b8152818360068301375f9101600601908152919050565b5f60ff821660ff81036146e9576146e9613da0565b60010192915050565b606081525f61470560608301868861421c565b828103602084015261471781866139a2565b91505082604083015295945050505050565b66028a096a0a49d160cd1b81525f612ebc600783018461436c565b65028a096a19d160d51b81525f612ebc600683018461436c565b65028a096ab1d160d51b81525f612ebc600683018461436c565b6affffffffffffffffffffff818116838216029081169081811461479e5761479e613da0565b5092915050565b5f602082840312156147b5575f5ffd5b815160068110612ebc575f5ffd5b5f602082840312156147d3575f5ffd5b81516001600160401b038111156147e8575f5ffd5b8201604081850312156147f9575f5ffd5b614801613eb0565b81516001600160401b03811115614816575f5ffd5b61482286828501613f24565b82525060208201516001600160401b0381111561483d575f5ffd5b80830192505084601f830112614851575f5ffd5b815161485f61453c826144cb565b8082825260208201915060208360051b860101925087831115614880575f5ffd5b602085015b838110156149515780516001600160401b038111156148a2575f5ffd5b860160a0818b03601f190112156148b7575f5ffd5b6148bf613ed2565b6148cb6020830161438e565b81526148d9604083016143a3565b60208201526060820151604082015260808201516001600160401b03811115614900575f5ffd5b61490f8c602083860101613f24565b60608301525060a08201516001600160401b0381111561492d575f5ffd5b61493c8c602083860101613f24565b60808301525084525060209283019201614885565b506020840152509095945050505050565b5f5f8335601e19843603018112614977575f5ffd5b8301803591506001600160401b03821115614990575f5ffd5b6020019150600581901b3603821315613a9e575f5ffd5b5f6149b2828661436c565b6101d160f51b8152838560028301375f930160020192835250909392505050565b608081525f6149e660808301878961421c565b85602084015282810360408401526149fe81866139a2565b9150508260608301529695505050505050565b5f614a1c828561436c565b6101d160f51b8152614a31600282018561436c565b95945050505050565b5f60208284031215614a4a575f5ffd5b5051919050565b606081525f614a6360608301866139a2565b602083019490945250901515604090910152919050565b5f5f5f5f60808587031215614a8d575f5ffd5b845160208601516040870151606088015192965090945092505f81900b8114613d81575f5ffd5b5f81614ac257614ac2613da0565b505f190190565b61010081525f614add61010083018a6139a2565b881515602084015287604084015286606084015285608084015282810360a0840152614b2481600e81526d4563686f4d61726b65744461746160901b602082015260400190565b60c0840195909552505060e001529594505050505056fea26469706673582212209b8baf72f27510b21011fcba0a311b4bca7eab9b383c47acd0f97555f23a7b1b64736f6c634300081e0033000000000000000000000000787fb122fc8a4b183e42e92dd48d1b52f707e031
Deployed Bytecode
0x608060405234801561000f575f5ffd5b506004361061009b575f3560e01c8063aa8ff04e11610063578063aa8ff04e1461018d578063b5ecf912146101c2578063b6967b3b146101e3578063cfce1a99146101f6578063efdbb6311461021d575f5ffd5b806310cf02b91461009f578063431facbc146100f5578063603ab8b71461011757806395953ee11461012c5780639bff4ada1461016b575b5f5ffd5b6100db6100ad366004613970565b6001600160a01b03165f908152600560205260409020546001600160801b03600160801b8204811692911690565b604080519283526020830191909152015b60405180910390f35b61010861010336600461398b565b610230565b6040516100ec939291906139d0565b61012a610125366004613aa5565b6106ba565b005b6101537f0000000000000000000000008f1e629536c74e8d8f58cfcbb44ad9cdf66a243381565b6040516001600160a01b0390911681526020016100ec565b61017e610179366004613aec565b6109c8565b6040516100ec93929190613b1a565b6101a061019b366004613b7d565b610ec5565b60408051941515855260208501939093529183015260608201526080016100ec565b6101d56101d036600461398b565b610ff1565b6040516100ec929190613bfd565b61012a6101f1366004613c60565b6110af565b6101537f000000000000000000000000787fb122fc8a4b183e42e92dd48d1b52f707e03181565b61012a61022b366004613d0e565b612016565b6003545f908190606090156106b357600354600b11156103df576003546001600160401b0381111561026457610264613d8c565b6040519080825280602002602001820160405280156102a957816020015b604080518082019091525f8152606060208201528152602001906001900390816102825790505b5090505f5b6003805482906102c090600190613db4565b6102ca9190613db4565b815481106102da576102da613dc7565b5f9182526020918290206040805180820190915260029092020180546001600160a01b03168252600181018054929391929184019161031890613ddb565b80601f016020809104026020016040519081016040528092919081815260200182805461034490613ddb565b801561038f5780601f106103665761010080835404028352916020019161038f565b820191905f5260205f20905b81548152906001019060200180831161037257829003601f168201915b5050505050815250508282815181106103aa576103aa613dc7565b60209081029190910101526003546103c490600190613db4565b81146103d2576001016102ae565b60018093509350506106b3565b835f036103eb57600193505b6003546103fa90600a90613e27565b91505f61040883600a613e46565b6003546104159190613db4565b60035490915061042684600a613e46565b101561043a578261043681613e5d565b9350505b82851115610446578294505b8493505f5f866001036104765760035461046290600190613db4565b915061046f600a83613db4565b9050610533565b8487036104a05782156104935761048e600184613db4565b610499565b60099150815b9150610533565b825f036104ad575f6104b8565b6104b8600184613db4565b6104c28887613db4565b6104cd90600a613e46565b6104d79190613e75565b6104e19083613e75565b9150825f036104f0575f6104fb565b6104fb600184613db4565b60016105078988613db4565b6105119190613db4565b61051c90600a613e46565b6105269190613e75565b6105309082613e75565b90505b8061053f836001613e75565b6105499190613db4565b6001600160401b0381111561056057610560613d8c565b6040519080825280602002602001820160405280156105a557816020015b604080518082019091525f81526060602082015281526020019060019003908161057e5790505b5093505f5b600383815481106105bd576105bd613dc7565b5f9182526020918290206040805180820190915260029092020180546001600160a01b0316825260018101805492939192918401916105fb90613ddb565b80601f016020809104026020016040519081016040528092919081815260200182805461062790613ddb565b80156106725780601f1061064957610100808354040283529160200191610672565b820191905f5260205f20905b81548152906001019060200180831161065557829003601f168201915b50505050508152505085828151811061068d5761068d613dc7565b6020026020010181905250818303156106ae575f19909201916001016105aa565b505050505b9193909250565b5f6001600160a01b03167f0000000000000000000000008f1e629536c74e8d8f58cfcbb44ad9cdf66a24336001600160a01b0316632c960900336001600160a01b03166306fdde036040518163ffffffff1660e01b81526004015f60405180830381865afa15801561072e573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526107559190810190613f8f565b6040518263ffffffff1660e01b81526004016107719190613fc0565b606060405180830381865afa15801561078c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107b09190613fd2565b602001516001600160a01b0316036107db576040516395383ea160e01b815260040160405180910390fd5b600483836040516107ed92919061404e565b9081526040805160209281900383019020335f908152925290205460ff1615610829576040516321947e9960e21b815260040160405180910390fd5b60036040518060400160405280336001600160a01b0316815260200185858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920182905250939094525050835460018082018655948252602091829020845160029092020180546001600160a01b0319166001600160a01b03909216919091178155908301519293909290830191506108ca90826140a9565b5050506001600484846040516108e192919061404e565b9081526040805160209281900383019020335f908152908352818120805460ff191694151594909417909355600582528220600190810180549182018155835291200161092f838583614163565b50335f90815260056020526040812080546001600160801b03428116600160801b029116179055600180549161096483613e5d565b9091555050335f8181526005602052604090819020600190810154905491519092429290917fe50964cbc470160e6c3556ba4ea3ba3e84778d99446d737c1d1a8a4c9c50eb03916109bb9189918991899190614244565b60405180910390a4505050565b6001600160a01b0381165f90815260056020526040812060010154819060609015610ebe576001600160a01b0384165f90815260056020526040902060010154600b1115610bb2576001600160a01b0384165f908152600560205260409020600101546001600160401b03811115610a4257610a42613d8c565b604051908082528060200260200182016040528015610a7557816020015b6060815260200190600190039081610a605790505b5090505f5b6001600160a01b0385165f9081526005602052604090206001908101805490918391610aa69190613db4565b610ab09190613db4565b81548110610ac057610ac0613dc7565b905f5260205f20018054610ad390613ddb565b80601f0160208091040260200160405190810160405280929190818152602001828054610aff90613ddb565b8015610b4a5780601f10610b2157610100808354040283529160200191610b4a565b820191905f5260205f20905b815481529060010190602001808311610b2d57829003601f168201915b5050505050828281518110610b6157610b61613dc7565b6020908102919091018101919091526001600160a01b0386165f908152600590915260409020600190810154610b979190613db4565b8114610ba557600101610a7a565b6001809350935050610ebe565b845f03610bbe57600194505b6001600160a01b0384165f90815260056020526040902060010154610be590600a90613e27565b91505f610bf383600a613e46565b6001600160a01b0386165f90815260056020526040902060010154610c189190613db4565b6001600160a01b0386165f90815260056020526040902060010154909150610c4184600a613e46565b1015610c555782610c5181613e5d565b9350505b82861115610c61578295505b8593505f5f87600103610ca9576001600160a01b0387165f908152600560205260409020600190810154610c959190613db4565b9150610ca2600a83613db4565b9050610d66565b848803610cd3578215610cc657610cc1600184613db4565b610ccc565b60099150815b9150610d66565b825f03610ce0575f610ceb565b610ceb600184613db4565b610cf58987613db4565b610d0090600a613e46565b610d0a9190613e75565b610d149083613e75565b9150825f03610d23575f610d2e565b610d2e600184613db4565b6001610d3a8a88613db4565b610d449190613db4565b610d4f90600a613e46565b610d599190613e75565b610d639082613e75565b90505b80610d72836001613e75565b610d7c9190613db4565b6001600160401b03811115610d9357610d93613d8c565b604051908082528060200260200182016040528015610dc657816020015b6060815260200190600190039081610db15790505b5093505f5b6001600160a01b0388165f908152600560205260409020600101805484908110610df757610df7613dc7565b905f5260205f20018054610e0a90613ddb565b80601f0160208091040260200160405190810160405280929190818152602001828054610e3690613ddb565b8015610e815780601f10610e5857610100808354040283529160200191610e81565b820191905f5260205f20905b815481529060010190602001808311610e6457829003601f168201915b5050505050858281518110610e9857610e98613dc7565b602002602001018190525081830315610eb9575f1990920191600101610dcb565b505050505b9250925092565b5f5f5f5f60048888604051610edb92919061404e565b9081526040805191829003602090810183206001600160a01b038d165f9081529152205460ff169450600490610f14908a908a9061404e565b90815260408051602092819003830181206001600160a01b038d165f9081529352912054610100900460ff169350600490610f52908a908a9061404e565b90815260408051602092819003830190206001600160a01b038c165f90815292529020546201000090046001600160f01b031691508415610fe55760048888604051610f9f92919061404e565b9081526040805191829003602090810183206001600160a01b038d165f90815291522060020190610fd3908890889061404e565b90815260200160405180910390205490505b95509550955095915050565b60038181548110611000575f80fd5b5f918252602090912060029091020180546001820180546001600160a01b0390921693509061102e90613ddb565b80601f016020809104026020016040519081016040528092919081815260200182805461105a90613ddb565b80156110a55780601f1061107c576101008083540402835291602001916110a5565b820191905f5260205f20905b81548152906001019060200180831161108857829003601f168201915b5050505050905082565b821580156110bb575080155b156110d957604051635b9e1d4560e11b815260040160405180910390fd5b5f7f000000000000000000000000787fb122fc8a4b183e42e92dd48d1b52f707e0316001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa158015611136573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061115a919061429c565b604051632ac1326b60e01b815233600482018190526001600160a01b039283161492507f000000000000000000000000787fb122fc8a4b183e42e92dd48d1b52f707e03190911690632ac1326b90602401602060405180830381865afa1580156111c6573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111ea91906142b7565b80156111f4575080155b1561121257604051631e4230c760e21b815260040160405180910390fd5b6004878760405161122492919061404e565b90815260408051602092819003830190206001600160a01b038b165f908152925290205460ff16611275578686604051633c00a7af60e01b815260040161126c9291906142d6565b60405180910390fd5b606084156114f1575f5f5b868110156114ee5787878281811061129a5761129a613dc7565b90506020028101906112ac91906142e9565b6112b69080614307565b6040516020016112c7929190614349565b60405160208183030381529060405292507f000000000000000000000000787fb122fc8a4b183e42e92dd48d1b52f707e0316001600160a01b031663b6001e2460018560405160200161131a9190614383565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b81526004016113619291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa15801561137c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113a091906143b9565b94505050506001600160a01b03821633148015906113bc575083155b156113df578260405160016218620560e01b0319815260040161126c9190613fc0565b8787828181106113f1576113f1613dc7565b905060200281019061140391906142e9565b611411906020810190614307565b61141a91614406565b60405160049061142d908d908d9061404e565b90815260200160405180910390205f8d6001600160a01b03166001600160a01b031681526020019081526020015f206002018460405161146d9190614383565b90815260405190819003602001902055336001600160a01b038c167fd7b65b61e10654aaf9ab3a7af2b11e30c5c7e3b2ce4dfe8378f8ca14687c2d1e8c8c8c8c878181106114bd576114bd613dc7565b90506020028101906114cf91906142e9565b6040516114de93929190614464565b60405180910390a3600101611280565b50505b821561200b575f896001600160a01b03166326f016376040518163ffffffff1660e01b81526004015f60405180830381865afa158015611533573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261155a91908101906144f9565b5190508484905060048a8a60405161157392919061404e565b90815260408051602092819003830190206001600160a01b038e165f90815292529020546115a990610100900460ff1683613db4565b141580156115b5575082155b156115d7578888604051635170461160e11b815260040161126c9291906142d6565b5f5f5b85811015611ba1575f8787838181106115f5576115f5613dc7565b90506020028101906116079190614641565b611615906020810190614655565b60028111156116265761162661462d565b036116845786868281811061163d5761163d613dc7565b905060200281019061164f9190614641565b61165d906040810190614307565b60405160200161166e929190614670565b6040516020818303038152906040529350611766565b600187878381811061169857611698613dc7565b90506020028101906116aa9190614641565b6116b8906020810190614655565b60028111156116c9576116c961462d565b03611711578686828181106116e0576116e0613dc7565b90506020028101906116f29190614641565b611700906040810190614307565b60405160200161166e929190614692565b86868281811061172357611723613dc7565b90506020028101906117359190614641565b611743906040810190614307565b6040516020016117549291906146b3565b60405160208183030381529060405293505b60405163aa588b4b60e01b81526001600160a01b038d169063aa588b4b90611792908790600401613fc0565b602060405180830381865afa1580156117ad573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117d191906142b7565b6117f05783604051633cfbd52760e21b815260040161126c9190613fc0565b7f000000000000000000000000787fb122fc8a4b183e42e92dd48d1b52f707e0316001600160a01b031663b6001e2433866040516020016118319190614383565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b81526004016118789291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa158015611893573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118b791906143b9565b50919350600291506118c69050565b8260038111156118d8576118d861462d565b141580156118e4575084155b15611907578360405160016218620560e01b0319815260040161126c9190613fc0565b620186a087878381811061191d5761191d613dc7565b905060200281019061192f9190614641565b602001351180611964575086868281811061194c5761194c613dc7565b905060200281019061195e9190614641565b60200135155b156119ad5786868281811061197b5761197b613dc7565b905060200281019061198d9190614641565b602001356040516370e00d7760e01b815260040161126c91815260200190565b60048b8b6040516119bf92919061404e565b90815260200160405180910390205f8d6001600160a01b03166001600160a01b031681526020019081526020015f20600201846040516119ff9190614383565b9081526020016040518091039020545f14611a3b5784611a36578a8a604051635170461160e11b815260040161126c9291906142d6565b611aa0565b60048b8b604051611a4d92919061404e565b90815260408051602092819003830190206001600160a01b038f165f908152925290208054610100900460ff16906001611a86836146d4565b91906101000a81548160ff021916908360ff160217905550505b868682818110611ab257611ab2613dc7565b9050602002810190611ac49190614641565b6020013560048c8c604051611ada92919061404e565b90815260200160405180910390205f8e6001600160a01b03166001600160a01b031681526020019081526020015f2060020185604051611b1a9190614383565b90815260405190819003602001902055336001600160a01b038d167fb72e5373934391ba6c4fc5b9542ee21c20a5d890a85cfdd16d446357e83af2888d8d888c8c88818110611b6b57611b6b613dc7565b9050602002810190611b7d9190614641565b60200135604051611b9194939291906146f2565b60405180910390a36001016115da565b8260048c8c604051611bb492919061404e565b90815260200160405180910390205f8e6001600160a01b03166001600160a01b031681526020019081526020015f205f0160019054906101000a900460ff1660ff1603611f50575f670de0b6b3a764000090505f8d6001600160a01b03166326f016376040518163ffffffff1660e01b81526004015f60405180830381865afa158015611c43573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611c6a91908101906144f9565b90505f8d8d8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f81840152601f19601f8201169050808301925050505050505090505f8f90505f94505b8251851015611ed1575f838681518110611cd757611cd7613dc7565b60200260200101515f01516002811115611cf357611cf361462d565b03611d3c57828581518110611d0a57611d0a613dc7565b602002602001015160600151604051602001611d269190614729565b6040516020818303038152906040529750611ddf565b6001838681518110611d5057611d50613dc7565b60200260200101515f01516002811115611d6c57611d6c61462d565b03611d9f57828581518110611d8357611d83613dc7565b602002602001015160600151604051602001611d269190614744565b828581518110611db157611db1613dc7565b602002602001015160600151604051602001611dcd919061475e565b60405160208183030381529060405297505b611e9d611e96611e28858881518110611dfa57611dfa613dc7565b602002602001015160200151662386f26fc10000611e189190614778565b6affffffffffffffffffffff1690565b611e90600486604051611e3b9190614383565b9081526040805191829003602090810183206001600160a01b0389165f90815291522060020190611e6d908e90614383565b9081526020016040518091039020546509184e72a000611e8d9190613e46565b90565b90612da0565b8590612eae565b93507f00000000000000000000000000000000000000000000000000000000000000008414611ed157600190940193611cbb565b83600483604051611ee29190614383565b90815260408051602092819003830190206001600160a01b0385165f908152925290206001015583816001600160a01b03167ffd61a2d01d4536539eb3388a9803c27ef3a5b28bb51773f83534e77bf02770d784604051611f439190613fc0565b60405180910390a3505050505b611fbe60048c8c604051611f6592919061404e565b90815260200160405180910390205f8e6001600160a01b03166001600160a01b031681526020019081526020015f20600101547f0000000000000000000000000000000000000000000000000000000000000000141590565b15612007578b6001600160a01b03167ffb227420d8844fd0693b8044d6444d0d36e18e02034526f9d379c05612e719838c8c604051611ffe9291906142d6565b60405180910390a25b5050505b505050505050505050565b5f7f000000000000000000000000787fb122fc8a4b183e42e92dd48d1b52f707e0316001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa158015612073573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612097919061429c565b604051632ac1326b60e01b815233600482018190526001600160a01b039283161492507f000000000000000000000000787fb122fc8a4b183e42e92dd48d1b52f707e03190911690632ac1326b90602401602060405180830381865afa158015612103573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061212791906142b7565b8015612131575080155b1561214f57604051631e4230c760e21b815260040160405180910390fd5b6004848460405161216192919061404e565b90815260408051602092819003830190206001600160a01b0388165f908152925290205460ff166121a9578383604051633c00a7af60e01b815260040161126c9291906142d6565b5f856001600160a01b031663ef8a92356040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121e6573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061220a91906147a5565b905060018160058111156122205761222061462d565b141580156122405750600281600581111561223d5761223d61462d565b14155b801561225e5750600381600581111561225b5761225b61462d565b14155b8015612268575081155b156122865760405163700f030f60e01b815260040160405180910390fd5b6122c56004868660405161229b92919061404e565b90815260408051602092819003830190206001600160a01b038a165f908152925290206001015490565b5f036122e4576040516306b2e62960e11b815260040160405180910390fd5b5f866001600160a01b0316638c7a8eb36040518163ffffffff1660e01b81526004015f60405180830381865afa158015612320573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261234791908101906147c3565b90505f6123548580614307565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505060405192935061239c928492506020019050614383565b60408051601f19818403018152908290528051602091820120845190926123c39201614383565b60405160208183030381529060405280519060200120146123f757604051637128647b60e11b815260040160405180910390fd5b5f60605f5b6124096020890189614962565b9050831015612728578361242060208a018a614962565b8581811061243057612430613dc7565b905060200281019061244291906142e9565b61244c9080614307565b60405160200161245e939291906149a7565b60408051601f198184030181529082905263aa588b4b60e01b825292506001600160a01b038c169063aa588b4b9061249a908590600401613fc0565b602060405180830381865afa1580156124b5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124d991906142b7565b6124f85781604051631f3f577f60e21b815260040161126c9190613fc0565b7f000000000000000000000000787fb122fc8a4b183e42e92dd48d1b52f707e0316001600160a01b031663b6001e245f846040516020016125399190614383565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b81526004016125809291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa15801561259b573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125bf91906143b9565b93505050336001600160a01b03831614801591506125db575086155b156125fe578160405160016218620560e01b0319815260040161126c9190613fc0565b61260b6020890189614962565b8481811061261b5761261b613dc7565b905060200281019061262d91906142e9565b6020013560048b8b60405161264392919061404e565b90815260200160405180910390205f8d6001600160a01b03166001600160a01b031681526020019081526020015f20600201836040516126839190614383565b908152602001604051809103902081905550336001600160a01b03168b6001600160a01b03167f9e9ec73da222bb34e89c444ee2841702e1509018ad901b4deb68b0ef9b859f398c8c42878e80602001906126de9190614962565b8b8181106126ee576126ee613dc7565b905060200281019061270091906142e9565b602001356040516127159594939291906149d3565b60405180910390a36001909201916123fc565b6001600160a01b038b165f90815260056020526040812080546fffffffffffffffffffffffffffffffff1916426001600160801b03161790559250825f8b8b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152509293508f925050505b87602001515186101561297d5786886020015187815181106127c4576127c4613dc7565b6020026020010151606001516040516020016127e1929190614a11565b60405160208183030381529060405294506004826040516128029190614383565b9081526040805191829003602090810183206001600160a01b0385165f90815291522060020190612834908790614383565b9081526020016040518091039020548860200151878151811061285957612859613dc7565b6020026020010151602001516001600160f81b0316111561289c577f0000000000000000000000000000000000000000000000000000000000000000925061297d565b61297061296961292a6128cd8b602001518a815181106128be576128be613dc7565b60200260200101516040015190565b6129246004876040516128e09190614383565b9081526040805191829003602090810183206001600160a01b038a165f90815291522060020190612912908c90614383565b90815260200160405180910390205490565b90612ec3565b6129638b602001518a8151811061294357612943613dc7565b60200260200101515f015160ff16662386f26fc10000611e8d9190613e46565b90612eae565b8490612eda565b60019096019592506127a0565b82816001600160a01b03167fc8d7d7296c924c3920078949d915bb20c80351022f3d90dda892192ad65cd974846040516129b79190613fc0565b60405180910390a35f5f5f612a30846001600160a01b03166324034a0e876040518263ffffffff1660e01b81526004016129f19190613fc0565b602060405180830381865afa158015612a0c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e8d9190614a3a565b92505f9150600190507f00000000000000000000000000000000000000000000000000000000000000008314158015612a8857507f000000000000000000000000000000000000000000000000000000000000000086145b15612a9457505f612bbc565b7f000000000000000000000000000000000000000000000000000000000000000083148015612ae357507f00000000000000000000000000000000000000000000000000000000000000008614155b15612b3057612b29600486604051612afb9190614383565b90815260408051602092819003830190206001600160a01b0388165f90815292529020600101548790612eae565b9150612bbc565b7f00000000000000000000000000000000000000000000000000000000000000008314158015612b8057507f00000000000000000000000000000000000000000000000000000000000000008614155b15612bbc57612b98600486604051612afb9190614383565b915081831015612bac57612b298284612ee8565b612bb68383612ee8565b91505f90505b7f00000000000000000000000000000000000000000000000000000000000000008214612d8d575f8080806001600160a01b03881663a7c64c708a88886040518463ffffffff1660e01b8152600401612c1793929190614a51565b6080604051808303815f875af1158015612c33573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c579190614a7a565b604051939750919550935091504290600490612c74908c90614383565b90815260408051602092819003830190206001600160a01b038c165f90815292528120805461ffff16620100006001600160f01b0394909416939093029290921790915581900b15612cf857805f0b600114612ce25760028054905f612cd983614ab4565b91905055612cf6565b60028054905f612cf183613e5d565b919050555b505b84612d1957855f80548190612d0e908490613db4565b925050819055612d31565b855f80548190612d2a908490613e75565b9250508190555b508286896001600160a01b03167fc75368439f060eb528fcbcf2dcce8dbca5f2e6a36d590b59a7d354d4c6ce96f28c898988425f600201545f5f0154604051612d809796959493929190614ac9565b60405180910390a4505050505b5050505050505050505050505050505050565b5f8282818303612dc9578015612db6575f612dc0565b670de0b6b3a76400005b92505050612ea8565b670de0b6b3a76400008203612dea57670de0b6b3a764000092505050612ea8565b805f03612e0357670de0b6b3a764000092505050612ea8565b670de0b6b3a76400008103612e1c578492505050612ea8565b670de0b6b3a7640000821115612e4d57612e46612e41612e3b87612ef6565b86612eae565b61301d565b9250612ea5565b5f612e6a611e8d846ec097ce7bc90715b34b9f1000000000613e27565b90505f612e82612e41612e7c84612ef6565b88612eae565b9050612ea0611e8d826ec097ce7bc90715b34b9f1000000000613e27565b945050505b50505b92915050565b5f612ebc611e8d8484613079565b9392505050565b5f612ebc611e8d84670de0b6b3a76400008561312b565b5f612ebc611e8d8385613e75565b5f612ebc611e8d8385613db4565b5f81670de0b6b3a7640000811015612f245760405163036d32ef60e41b81526004810184905260240161126c565b5f612fa5670de0b6b3a7640000830460016001600160801b03821160071b91821c6001600160401b03811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c60ff8111600390811b91821c600f811160021b90811c918211871b91821c969096119490961792909217171791909117919091171790565b9050670de0b6b3a7640000810282821c670de0b6b3a763ffff198101612fce5750949350505050565b671bc16d674ec800006706f05b59d3b200005b801561301157670de0b6b3a7640000838002049250818310613009579283019260019290921c915b60011c612fe1565b50919695505050505050565b5f81680a688906bd8affffff81111561304c5760405163b3b6ba1f60e01b81526004810184905260240161126c565b5f613063670de0b6b3a7640000604084901b613e27565b9050613071611e8d826131f9565b949350505050565b5f80805f19848609848602925082811083820303915050805f036130aa5750670de0b6b3a764000090049050612ea8565b670de0b6b3a764000081106130dc57604051635173648d60e01b8152600481018690526024810185905260440161126c565b5f670de0b6b3a764000085870962040000818503049310909103600160ee1b02919091177faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106690291505092915050565b5f80805f19858709858702925082811083820303915050805f036131625783828161315857613158613e13565b0492505050612ebc565b83811061319357604051630c740aef60e31b815260048101879052602481018690526044810185905260640161126c565b5f84868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203025f889003889004909101858311909403939093029303949094049190911702949350505050565b600160bf1b67ff000000000000008216156133065767800000000000000082161561322d5768016a09e667f3bcc9090260401c5b67400000000000000082161561324c576801306fe0a31b7152df0260401c5b67200000000000000082161561326b576801172b83c7d517adce0260401c5b67100000000000000082161561328a5768010b5586cf9890f62a0260401c5b6708000000000000008216156132a9576801059b0d31585743ae0260401c5b6704000000000000008216156132c857680102c9a3e778060ee70260401c5b6702000000000000008216156132e75768010163da9fb33356d80260401c5b67010000000000000082161561330657680100b1afa5abcbed610260401c5b66ff0000000000008216156134055766800000000000008216156133335768010058c86da1c09ea20260401c5b6640000000000000821615613351576801002c605e2e8cec500260401c5b662000000000000082161561336f57680100162f3904051fa10260401c5b661000000000000082161561338d576801000b175effdc76ba0260401c5b66080000000000008216156133ab57680100058ba01fb9f96d0260401c5b66040000000000008216156133c95768010002c5cc37da94920260401c5b66020000000000008216156133e7576801000162e525ee05470260401c5b66010000000000008216156134055768010000b17255775c040260401c5b65ff00000000008216156134fb5765800000000000821615613430576801000058b91b5bc9ae0260401c5b6540000000000082161561344d57680100002c5c89d5ec6d0260401c5b6520000000000082161561346a5768010000162e43f4f8310260401c5b6510000000000082161561348757680100000b1721bcfc9a0260401c5b650800000000008216156134a45768010000058b90cf1e6e0260401c5b650400000000008216156134c1576801000002c5c863b73f0260401c5b650200000000008216156134de57680100000162e430e5a20260401c5b650100000000008216156134fb576801000000b1721835510260401c5b64ff000000008216156135e85764800000000082161561352457680100000058b90c0b490260401c5b6440000000008216156135405768010000002c5c8601cc0260401c5b64200000000082161561355c576801000000162e42fff00260401c5b6410000000008216156135785768010000000b17217fbb0260401c5b640800000000821615613594576801000000058b90bfce0260401c5b6404000000008216156135b057680100000002c5c85fe30260401c5b6402000000008216156135cc5768010000000162e42ff10260401c5b6401000000008216156135e857680100000000b17217f80260401c5b63ff0000008216156136cc57638000000082161561360f5768010000000058b90bfc0260401c5b634000000082161561362a576801000000002c5c85fe0260401c5b632000000082161561364557680100000000162e42ff0260401c5b6310000000821615613660576801000000000b17217f0260401c5b630800000082161561367b57680100000000058b90c00260401c5b63040000008216156136965768010000000002c5c8600260401c5b63020000008216156136b1576801000000000162e4300260401c5b63010000008216156136cc5768010000000000b172180260401c5b62ff00008216156137a757628000008216156136f1576801000000000058b90c0260401c5b6240000082161561370b57680100000000002c5c860260401c5b622000008216156137255768010000000000162e430260401c5b6210000082161561373f57680100000000000b17210260401c5b620800008216156137595768010000000000058b910260401c5b62040000821615613773576801000000000002c5c80260401c5b6202000082161561378d57680100000000000162e40260401c5b620100008216156137a7576801000000000000b1720260401c5b61ff00821615613879576180008216156137ca57680100000000000058b90260401c5b6140008216156137e35768010000000000002c5d0260401c5b6120008216156137fc576801000000000000162e0260401c5b6110008216156138155768010000000000000b170260401c5b61080082161561382e576801000000000000058c0260401c5b61040082161561384757680100000000000002c60260401c5b61020082161561386057680100000000000001630260401c5b61010082161561387957680100000000000000b10260401c5b60ff82161561394257608082161561389a57680100000000000000590260401c5b60408216156138b2576801000000000000002c0260401c5b60208216156138ca57680100000000000000160260401c5b60108216156138e2576801000000000000000b0260401c5b60088216156138fa57680100000000000000060260401c5b600482161561391257680100000000000000030260401c5b600282161561392a57680100000000000000010260401c5b600182161561394257680100000000000000010260401c5b670de0b6b3a76400000260409190911c60bf031c90565b6001600160a01b038116811461396d575f5ffd5b50565b5f60208284031215613980575f5ffd5b8135612ebc81613959565b5f6020828403121561399b575f5ffd5b5035919050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b5f606082018583528460208401526060604084015280845180835260808501915060808160051b8601019250602086015f5b82811015613a5357868503607f19018452815180516001600160a01b03168652602090810151604091870182905290613a3d908701826139a2565b9550506020938401939190910190600101613a02565b509298975050505050505050565b5f5f83601f840112613a71575f5ffd5b5081356001600160401b03811115613a87575f5ffd5b602083019150836020828501011115613a9e575f5ffd5b9250929050565b5f5f5f60408486031215613ab7575f5ffd5b83356001600160401b03811115613acc575f5ffd5b613ad886828701613a61565b909790965060209590950135949350505050565b5f5f60408385031215613afd575f5ffd5b823591506020830135613b0f81613959565b809150509250929050565b5f606082018583528460208401526060604084015280845180835260808501915060808160051b8601019250602086015f5b82811015613a5357607f19878603018452613b688583516139a2565b94506020938401939190910190600101613b4c565b5f5f5f5f5f60608688031215613b91575f5ffd5b8535613b9c81613959565b945060208601356001600160401b03811115613bb6575f5ffd5b613bc288828901613a61565b90955093505060408601356001600160401b03811115613be0575f5ffd5b613bec88828901613a61565b969995985093965092949392505050565b6001600160a01b03831681526040602082018190525f90613071908301846139a2565b5f5f83601f840112613c30575f5ffd5b5081356001600160401b03811115613c46575f5ffd5b6020830191508360208260051b8501011115613a9e575f5ffd5b5f5f5f5f5f5f5f6080888a031215613c76575f5ffd5b8735613c8181613959565b965060208801356001600160401b03811115613c9b575f5ffd5b613ca78a828b01613a61565b90975095505060408801356001600160401b03811115613cc5575f5ffd5b613cd18a828b01613c20565b90955093505060608801356001600160401b03811115613cef575f5ffd5b613cfb8a828b01613c20565b989b979a50959850939692959293505050565b5f5f5f5f60608587031215613d21575f5ffd5b8435613d2c81613959565b935060208501356001600160401b03811115613d46575f5ffd5b613d5287828801613a61565b90945092505060408501356001600160401b03811115613d70575f5ffd5b850160408188031215613d81575f5ffd5b939692955090935050565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b81810381811115612ea857612ea8613da0565b634e487b7160e01b5f52603260045260245ffd5b600181811c90821680613def57607f821691505b602082108103613e0d57634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52601260045260245ffd5b5f82613e4157634e487b7160e01b5f52601260045260245ffd5b500490565b8082028115828204841417612ea857612ea8613da0565b5f60018201613e6e57613e6e613da0565b5060010190565b80820180821115612ea857612ea8613da0565b604051608081016001600160401b0381118282101715613eaa57613eaa613d8c565b60405290565b604080519081016001600160401b0381118282101715613eaa57613eaa613d8c565b60405160a081016001600160401b0381118282101715613eaa57613eaa613d8c565b604051601f8201601f191681016001600160401b0381118282101715613f1c57613f1c613d8c565b604052919050565b5f82601f830112613f33575f5ffd5b81516001600160401b03811115613f4c57613f4c613d8c565b613f5f601f8201601f1916602001613ef4565b818152846020838601011115613f73575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f60208284031215613f9f575f5ffd5b81516001600160401b03811115613fb4575f5ffd5b61307184828501613f24565b602081525f612ebc60208301846139a2565b5f6060828403128015613fe3575f5ffd5b50604051606081016001600160401b038111828210171561400657614006613d8c565b60405282516001600160401b038116811461401f575f5ffd5b8152602083015161402f81613959565b6020820152604083015161404281613959565b60408201529392505050565b818382375f9101908152919050565b601f8211156140a457805f5260205f20601f840160051c810160208510156140825750805b601f840160051c820191505b818110156140a1575f815560010161408e565b50505b505050565b81516001600160401b038111156140c2576140c2613d8c565b6140d6816140d08454613ddb565b8461405d565b6020601f821160018114614108575f83156140f15750848201515b5f19600385901b1c1916600184901b1784556140a1565b5f84815260208120601f198516915b828110156141375787850151825560209485019460019092019101614117565b508482101561415457868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6001600160401b0383111561417a5761417a613d8c565b61418e836141888354613ddb565b8361405d565b5f601f8411600181146141bf575f85156141a85750838201355b5f19600387901b1c1916600186901b1783556140a1565b5f83815260208120601f198716915b828110156141ee57868501358255602094850194600190920191016141ce565b508682101561420a575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f61425760808301868861421c565b846020840152828103604084015261428a81600e81526d4563686f4d61726b65744461746160901b602082015260400190565b91505082606083015295945050505050565b5f602082840312156142ac575f5ffd5b8151612ebc81613959565b5f602082840312156142c7575f5ffd5b81518015158114612ebc575f5ffd5b602081525f61307160208301848661421c565b5f8235603e198336030181126142fd575f5ffd5b9190910192915050565b5f5f8335601e1984360301811261431c575f5ffd5b8301803591506001600160401b03821115614335575f5ffd5b602001915036819003821315613a9e575f5ffd5b67021a7a72324a39d160c51b8152818360088301375f9101600801908152919050565b5f81518060208401855e5f93019283525090919050565b5f612ebc828461436c565b805160ff8116811461439e575f5ffd5b919050565b80516001600160f81b038116811461439e575f5ffd5b5f5f5f5f608085870312156143cc575f5ffd5b8451600481106143da575f5ffd5b93506143e86020860161438e565b92506143f6604086016143a3565b91506060850151613d8181613959565b80356020831015612ea8575f19602084900360031b1b1692915050565b5f5f8335601e19843603018112614438575f5ffd5b83016020810192503590506001600160401b03811115614456575f5ffd5b803603821315613a9e575f5ffd5b604081525f61447760408301858761421c565b82810360208401526144898485614423565b6040835261449b60408401828461421c565b9150506144ab6020860186614423565b83830360208501526144be83828461421c565b9998505050505050505050565b5f6001600160401b038211156144e3576144e3613d8c565b5060051b60200190565b6003811061396d575f5ffd5b5f60208284031215614509575f5ffd5b81516001600160401b0381111561451e575f5ffd5b8201601f8101841361452e575f5ffd5b805161454161453c826144cb565b613ef4565b8082825260208201915060208360051b850101925086831115614562575f5ffd5b602084015b838110156146225780516001600160401b03811115614584575f5ffd5b85016080818a03601f19011215614599575f5ffd5b6145a1613e88565b60208201516145af816144ed565b815260408201516affffffffffffffffffffff811681146145ce575f5ffd5b602082015260608201516145e181613959565b604082015260808201516001600160401b038111156145fe575f5ffd5b61460d8b602083860101613f24565b60608301525084525060209283019201614567565b509695505050505050565b634e487b7160e01b5f52602160045260245ffd5b5f8235605e198336030181126142fd575f5ffd5b5f60208284031215614665575f5ffd5b8135612ebc816144ed565b66028a096a0a49d160cd1b8152818360078301375f9101600701908152919050565b65028a096a19d160d51b8152818360068301375f9101600601908152919050565b65028a096ab1d160d51b8152818360068301375f9101600601908152919050565b5f60ff821660ff81036146e9576146e9613da0565b60010192915050565b606081525f61470560608301868861421c565b828103602084015261471781866139a2565b91505082604083015295945050505050565b66028a096a0a49d160cd1b81525f612ebc600783018461436c565b65028a096a19d160d51b81525f612ebc600683018461436c565b65028a096ab1d160d51b81525f612ebc600683018461436c565b6affffffffffffffffffffff818116838216029081169081811461479e5761479e613da0565b5092915050565b5f602082840312156147b5575f5ffd5b815160068110612ebc575f5ffd5b5f602082840312156147d3575f5ffd5b81516001600160401b038111156147e8575f5ffd5b8201604081850312156147f9575f5ffd5b614801613eb0565b81516001600160401b03811115614816575f5ffd5b61482286828501613f24565b82525060208201516001600160401b0381111561483d575f5ffd5b80830192505084601f830112614851575f5ffd5b815161485f61453c826144cb565b8082825260208201915060208360051b860101925087831115614880575f5ffd5b602085015b838110156149515780516001600160401b038111156148a2575f5ffd5b860160a0818b03601f190112156148b7575f5ffd5b6148bf613ed2565b6148cb6020830161438e565b81526148d9604083016143a3565b60208201526060820151604082015260808201516001600160401b03811115614900575f5ffd5b61490f8c602083860101613f24565b60608301525060a08201516001600160401b0381111561492d575f5ffd5b61493c8c602083860101613f24565b60808301525084525060209283019201614885565b506020840152509095945050505050565b5f5f8335601e19843603018112614977575f5ffd5b8301803591506001600160401b03821115614990575f5ffd5b6020019150600581901b3603821315613a9e575f5ffd5b5f6149b2828661436c565b6101d160f51b8152838560028301375f930160020192835250909392505050565b608081525f6149e660808301878961421c565b85602084015282810360408401526149fe81866139a2565b9150508260608301529695505050505050565b5f614a1c828561436c565b6101d160f51b8152614a31600282018561436c565b95945050505050565b5f60208284031215614a4a575f5ffd5b5051919050565b606081525f614a6360608301866139a2565b602083019490945250901515604090910152919050565b5f5f5f5f60808587031215614a8d575f5ffd5b845160208601516040870151606088015192965090945092505f81900b8114613d81575f5ffd5b5f81614ac257614ac2613da0565b505f190190565b61010081525f614add61010083018a6139a2565b881515602084015287604084015286606084015285608084015282810360a0840152614b2481600e81526d4563686f4d61726b65744461746160901b602082015260400190565b60c0840195909552505060e001529594505050505056fea26469706673582212209b8baf72f27510b21011fcba0a311b4bca7eab9b383c47acd0f97555f23a7b1b64736f6c634300081e0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000787fb122fc8a4b183e42e92dd48d1b52f707e031
-----Decoded View---------------
Arg [0] : echoAdministration (address): 0x787FB122fC8a4B183e42e92DD48D1b52F707e031
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000787fb122fc8a4b183e42e92dd48d1b52f707e031
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 ]
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.