Source Code
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Cross-Chain Transactions
Loading...
Loading
Contract Name:
EchoCampaign
Compiler Version
v0.8.30+commit.73712a01
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
// ███████╗ ██████╗██╗ ██╗ ██████╗ ███╗ ███╗ █████╗ ██████╗ ██╗ ██╗███████╗████████╗
// ██╔════╝██╔════╝██║ ██║██╔═══██╗ ████╗ ████║██╔══██╗██╔══██╗██║ ██╔╝██╔════╝╚══██╔══╝
// █████╗ ██║ ███████║██║ ██║ ██╔████╔██║███████║██████╔╝█████╔╝ █████╗ ██║
// ██╔══╝ ██║ ██╔══██║██║ ██║ ██║╚██╔╝██║██╔══██║██╔══██╗██╔═██╗ ██╔══╝ ██║
// ███████╗╚██████╗██║ ██║╚██████╔╝ ██║ ╚═╝ ██║██║ ██║██║ ██║██║ ██╗███████╗ ██║
// ╚══════╝ ╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚═╝
// ================================= EchoCampaign V2 =================================
// =================================== Summer 2025 ===================================
import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IEchoAdministration} from "../../interfaces/IEchoAdministration.sol";
import {IEchoCampaignFactory} from "../../interfaces/IEchoCampaignFactory.sol";
import {IEchoContents} from "../../interfaces/IEchoContents.sol";
import {IEchoCampaign, QualificationData, BudgetInfo, SocialKPIs, QA_METHOD, Status, Finalizer, Token, PostInfo, BasicInfo, CampaignInitData, KPI} from "../../interfaces/IEchoCampaign.sol";
import {Clone} from "../../libs/Clone.sol";
import {UD60x18, ud, unwrap} from "@prb/math/src/UD60x18.sol";
/// @title Echo Campaign contract
/// @author Dynabits.org
contract EchoCampaign is IEchoCampaign, Clone {
/********************************\
|-*-*-*-*-* STATES *-*-*-*-*-|
\********************************/
Status private _status;
bool private no_mid_refunding;
bool private _campaignPaused;
uint232 private _totalPosts;
uint256 private _totalEffectiveKPIs;
uint256 private _totalEligibleContents;
uint256 private _totalPaidAmount;
string private _ipfsCID;
BudgetInfo private _budgetInfo;
SocialKPIs private _socialKPIs;
QualificationData[] private _qaData;
mapping(string => bool) public registeredActionKind;
mapping(string => uint256) public contentEffectiveKPI;
/*******************************\
|-*-*-*-* CONSTANTS *-*-*-*-|
\*******************************/
address private immutable FRAX_USD;
// FOR PRODUCTION
// 0xFc00000000000000000000000000000000000001;
// FOR TESTNET
// 0xa35Bd0843e49F4eCF7Fb716FDADdc86f4482dB8E;
// FOR LOCAL TEST
// 0xecE1B93b2dA75C6EE3b78D158416df18Ae9FEE15;
/********************************\
|-*-*-*-*-* EVENTS *-*-*-*-*-|
\********************************/
event CampaignPauseToggled(bool indexed paused, address campaign);
event BudgetIncreased(
uint256 indexed addedAmount,
uint256 indexed lastBudget,
uint256 indexed newBudget,
address donator,
address campaign
);
event CampaignFinalized(
address indexed finalizer,
Finalizer indexed finalizerType,
uint256 indexed refundedAmount,
uint256 newBudget,
address campaign
);
event Claimed(
address indexed contentCreator,
uint256 indexed rewardedAmount,
uint256 indexed refundAmount,
uint256 totalPaidAmount,
string contentLink,
address campaign
);
event CampaignFinished(uint256 indexed finishedAt, address campaign);
/********************************\
|-*-*-*-*-* ERRORS *-*-*-*-*-|
\********************************/
error ONLY_FACTORY();
error ONLY_CONTENTS();
error ONLY_PROTOCOL_ADMIN();
error ONLY_OWNER();
error ONLY_FINALIZER_ORACLE();
error DEPRECATED_MODEL();
error CAMPAIGN_PAUSED();
error INVALID_CONTENT_LINK();
error NO_CONTENT_AUTHOR();
error DUPLICATE_QA(string qa);
error DUPLICATE_KPI(string kpi);
error ONLY_IN_PROGRESS_STATUS(Status status);
error ONLY_UPCOMING_OR_IN_PROGRESS_STATUS(Status status);
error ONLY_FINISHED_CAMPAIGN_STATUS();
error ONLY_CLAIMABLE_STATUS();
error NOT_ELIGIBLE_FOR_WITHDRAW();
error CHECK_QAS_OR_KPIS();
error GIVEN_QA_KIND_DOESNT_EXIST_ON_CAMPAIGN(string qaKind);
error GIVEN_KPI_KIND_DOESNT_EXIST_ON_CAMPAIGN(string kpiKind);
/*******************************\
|-*-*-*-* MODIFIERS *-*-*-*-|
\*******************************/
modifier onlyContents() {
if (msg.sender != contents()) revert ONLY_CONTENTS();
_;
}
modifier onlyProtocolAdmin() {
if (msg.sender != IEchoAdministration(admin()).protocolAdmin())
revert ONLY_PROTOCOL_ADMIN();
_;
}
modifier onlyOwner() {
if (msg.sender != owner()) revert ONLY_OWNER();
_;
}
modifier onlyFinalizerOracle() {
(, , , address finalizerOracle) = IEchoAdministration(admin())
.modelData(
address(1),
keccak256(abi.encodePacked("CONFIG: Finalizer"))
);
if (msg.sender != finalizerOracle) revert ONLY_FINALIZER_ORACLE();
_;
}
modifier onlyInProgressStatus() {
if (currentStatus() != Status.inProgress)
revert ONLY_IN_PROGRESS_STATUS(currentStatus());
_;
}
/******************************\
|-*-*-*-* BUILT-IN *-*-*-*-|
\******************************/
constructor() {
if (block.chainid == 252)
FRAX_USD = 0xFc00000000000000000000000000000000000001;
else if (block.chainid == 2522)
FRAX_USD = 0xa35Bd0843e49F4eCF7Fb716FDADdc86f4482dB8E;
else FRAX_USD = 0xecE1B93b2dA75C6EE3b78D158416df18Ae9FEE15;
}
/// @inheritdoc IEchoCampaign
function init(
string calldata ipfsCID,
BudgetInfo calldata budgetInfo_,
SocialKPIs calldata socialKPIs_,
QualificationData[] calldata qaData_
) external {
// Ensure only the factory contract can initialize.
if (msg.sender != factory()) revert ONLY_FACTORY();
if (
keccak256(abi.encodePacked(socialKPIs_.social)) ==
keccak256(abi.encodePacked("X"))
) revert DEPRECATED_MODEL();
// Store campaign metadata.
_ipfsCID = ipfsCID;
_budgetInfo = budgetInfo_;
_socialKPIs = socialKPIs_;
uint256 i;
string memory action;
// Register Qualification Actions.
while (i < qaData_.length) {
_qaData.push(qaData_[i]);
// Determine the QA method prefix.
if (qaData_[i].method == QA_METHOD.AI) action = "QA-AI: ";
else if (qaData_[i].method == QA_METHOD.COMMUNITY)
action = "QA-C: ";
else action = "QA-V: ";
// Revert the operation if a duplicate qualification is detected.
if (registeredActionKind[string.concat(action, qaData_[i].kind)])
revert DUPLICATE_QA(string.concat(action, qaData_[i].kind));
// Register the Qualification.
registeredActionKind[string.concat(action, qaData_[i].kind)] = true;
unchecked {
i++;
}
}
// Register Social KPI Actions.
delete i;
action = string.concat(socialKPIs_.social, ": ");
while (i < socialKPIs_.kpis.length) {
// Revert the operation if a duplicate KPI is detected.
if (
registeredActionKind[
string.concat(action, socialKPIs_.kpis[i].method)
]
)
revert DUPLICATE_KPI(
string.concat(action, socialKPIs_.kpis[i].method)
);
// Register the SocialKPI.
registeredActionKind[
string.concat(action, socialKPIs_.kpis[i].method)
] = true;
unchecked {
i++;
}
}
}
/********************************\
|-*-*-* ADMINISTRATION *-*-*-|
\********************************/
/// @inheritdoc IEchoCampaign
function togglePauseCampaign() external onlyProtocolAdmin {
// Toggle the paused state of the campaign.
_campaignPaused = !_campaignPaused;
// Emit an event to notify the state change.
emit CampaignPauseToggled(_campaignPaused, address(this));
}
/// @inheritdoc IEchoCampaign
function finalizeInProgressCampaign()
external
onlyProtocolAdmin
onlyInProgressStatus
{
// Calculate and refund the possible remaining budget.
uint256 refundedAmount = _refundPossibleAmountLeft();
// Emit event indicating campaign finalization.
emit CampaignFinalized(
msg.sender,
Finalizer.protocolAdmin,
refundedAmount,
_budgetInfo.reservedAmount,
address(this)
);
}
/// @notice Recover ERC20 tokens
/// @param tokenAddress address of ERC20 token
/// @param tokenAmount amount to be withdrawn
function recoverERC20(address tokenAddress, uint256 tokenAmount)
external
onlyProtocolAdmin
{
// bytes4(keccak256(bytes('transfer(address,uint256)')));
(bool success, bytes memory data) = tokenAddress.call(
abi.encodeWithSelector(0xa9059cbb, msg.sender, tokenAmount)
);
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"TransferHelper: TRANSFER_FAILED"
);
}
/*******************************\
|*-*-*-* EXTERNALS *-*-*-*-*|
\*******************************/
/// @inheritdoc IEchoCampaign
function increaseBudget(uint256 amount) external {
// Ensure the campaign is in an `upcoming` or `inProgress` status.
if (
currentStatus() != Status.upcoming &&
currentStatus() != Status.inProgress
) revert ONLY_UPCOMING_OR_IN_PROGRESS_STATUS(currentStatus());
// Transfer the funds from sender to contract.
IERC20(_budgetInfo.token).transferFrom(
msg.sender,
address(this),
amount
);
// Update the reserved budget amount.
unchecked {
_budgetInfo.reservedAmount += uint128(amount);
}
// Emit event notifying budget increase.
emit BudgetIncreased(
amount,
_budgetInfo.reservedAmount - amount,
_budgetInfo.reservedAmount,
msg.sender,
address(this)
);
}
/// @inheritdoc IEchoCampaign
function applyContent(string calldata contentLink)
external
onlyInProgressStatus
{
// Ensure the content link is not empty.
if (bytes(contentLink).length == 0) revert INVALID_CONTENT_LINK();
// Calculate the application fee.
uint256 totalFee = applicationFee();
// Transfer the application fee to protocol admin.
if (totalFee != 0)
IERC20(FRAX_USD).transferFrom(
msg.sender,
IEchoAdministration(admin()).protocolAdmin(),
totalFee
);
// Increment the total posts count.
_totalPosts++;
// Register the content link in the EchoContents contract.
IEchoContents(contents()).addContentLink(
contentLink,
_budgetInfo.maxPerPost
);
}
/// @inheritdoc IEchoCampaign
function finalizeFinishedCampaign() external onlyFinalizerOracle {
// Ensure campaign is in the 'finished' status.
if (currentStatus() != Status.finished)
revert ONLY_FINISHED_CAMPAIGN_STATUS();
// Calculate and refund the remaining budget.
uint256 refundedAmount = _refundPossibleAmountLeft();
// Emit event notifying campaign finalization.
emit CampaignFinalized(
msg.sender,
Finalizer.finalizerOracle,
refundedAmount,
_budgetInfo.reservedAmount,
address(this)
);
}
/// @inheritdoc IEchoCampaign
function updateKPIs(
string calldata contentLink,
uint256 additionalContentEffectiveKPI,
bool additional
)
external
onlyContents
returns (
uint256 totalEffectiveKPIs,
uint256 contentEffectiveKPI_,
uint256 totalEligibleContents,
int8 eligibilityPolarity
)
{
// If this is the first KPI update for the content, increase the eligible content count.
if (contentEffectiveKPI[contentLink] == 0) {
_totalEligibleContents++;
eligibilityPolarity = 1;
}
// Modify KPI based on the 'additional' flag.
if (additional) {
_totalEffectiveKPIs += additionalContentEffectiveKPI;
contentEffectiveKPI[contentLink] += additionalContentEffectiveKPI;
} else {
_totalEffectiveKPIs -= additionalContentEffectiveKPI;
contentEffectiveKPI[contentLink] -= additionalContentEffectiveKPI;
if (contentEffectiveKPI[contentLink] == 0) {
_totalEligibleContents--;
eligibilityPolarity--;
}
}
// Return updated KPI values.
totalEffectiveKPIs = _totalEffectiveKPIs;
contentEffectiveKPI_ = contentEffectiveKPI[contentLink];
totalEligibleContents = _totalEligibleContents;
}
/// @inheritdoc IEchoCampaign
function claim(string calldata contentLink) external {
// Ensure the campaign is in a claimable state.
if (_status != Status.claimable) revert ONLY_CLAIMABLE_STATUS();
// Retrieve content registration details and author's action kind.
(bool contentRegistered, , , uint256 actionKindRes) = IEchoContents(
contents()
).contentToCampaign(address(this), contentLink, "CONFIG: AuthorWallet");
// Revert if the content is not registered under this campaign.
if (!contentRegistered) revert INVALID_CONTENT_LINK();
// Ensure the content has an associated author registered by the oracle.
if (actionKindRes == 0) revert NO_CONTENT_AUTHOR();
// Check if the content has an effective KPI score and is eligible for rewards.
if (contentEffectiveKPI[contentLink] == 0)
revert NOT_ELIGIBLE_FOR_WITHDRAW();
// Calculate the reward amount based on the content's KPI ratio.
uint256 contentKPIratio = (contentEffectiveKPI[contentLink] * 1e6) /
_totalEffectiveKPIs;
uint256 rewardAmount = (_budgetInfo.reservedAmount * contentKPIratio) /
1e6;
uint256 refundAmount;
// Adjust reserved budget and determine refund amount if applicable.
if (_budgetInfo.maxPerPost == 0) {
if (_totalEligibleContents == 1)
rewardAmount = _budgetInfo.reservedAmount - _totalPaidAmount;
} else {
if (rewardAmount >= _budgetInfo.maxPerPost)
rewardAmount = _budgetInfo.maxPerPost;
if (_totalEligibleContents != 1) {
if (!no_mid_refunding && rewardAmount < _budgetInfo.maxPerPost)
refundAmount = _budgetInfo.maxPerPost - rewardAmount;
} else {
if (
rewardAmount < _budgetInfo.reservedAmount - _totalPaidAmount
)
refundAmount =
(_budgetInfo.reservedAmount - _totalPaidAmount) -
rewardAmount;
}
// Refund excess funds if applicable.
if (refundAmount != 0) {
IERC20(_budgetInfo.token).transfer(
refundAddress(),
refundAmount
);
}
}
// Transfer the calculated reward to the content's registered author.
IERC20(_budgetInfo.token).transfer(
address(uint160(actionKindRes)),
rewardAmount
);
// Remove the content's KPI score tracking and decrement the eligible content counter.
delete contentEffectiveKPI[contentLink];
_totalEligibleContents--;
_totalPaidAmount += rewardAmount + refundAmount;
// Emit an event to log the claim transaction.
emit Claimed(
address(uint160(actionKindRes)),
rewardAmount,
refundAmount,
_totalPaidAmount,
contentLink,
address(this)
);
// If all eligible claims have been processed, finalize the campaign.
if (_totalEligibleContents == 0) {
_status = Status.finalized;
delete _budgetInfo.reservedAmount;
emit CampaignFinished(block.timestamp, address(this));
}
}
/*****************************\
|-*-*-*-* GETTERS *-*-*-*-|
\*****************************/
/// @inheritdoc IEchoCampaign
function budgetInfo() external view returns (BudgetInfo memory) {
return _budgetInfo;
}
/// @inheritdoc IEchoCampaign
function socialKPIs() external view returns (SocialKPIs memory) {
return _socialKPIs;
}
/// @inheritdoc IEchoCampaign
function qaData() external view returns (QualificationData[] memory) {
return _qaData;
}
/// @inheritdoc IEchoCampaign
function rewardEstimation(
QualificationData[] calldata qas,
KPI[] calldata kpis
) external view returns (uint256) {
// Validate input arrays to ensure they are non-empty
// and match expected campaign `_qaData` & `_socialKPIs` lengths.
if (
qas.length == 0 ||
kpis.length == 0 ||
qas.length != _qaData.length ||
kpis.length != _socialKPIs.kpis.length
) revert CHECK_QAS_OR_KPIS();
uint256 i;
UD60x18 contentQaScore = ud(1e18);
string memory action;
// Determine the QA method prefix for action identification.
if (qas[i].method == QA_METHOD.AI) action = "QA-AI: ";
else if (qas[i].method == QA_METHOD.COMMUNITY) action = "QA-C: ";
else action = "QA-V: ";
// Calculate the content QA score based on provided qualifications.
while (i < qas.length) {
if (qas[i].pct == 0) break;
// Ensure the QA kind exists within the campaign.
if (!registeredActionKind[string.concat(action, qas[i].kind)])
revert GIVEN_QA_KIND_DOESNT_EXIST_ON_CAMPAIGN(
string.concat(action, qas[i].kind)
);
// Note: There is no explicit check for duplicate QA evaluations.
contentQaScore = contentQaScore.mul(
ud(qas[i].pct * 1e13).pow(ud(_qaData[i].pct * 1e16))
);
unchecked {
i++;
}
}
// If the loop did not process all QAs, return zero as a no reward case.
if (i != qas.length) return 0;
delete i;
action = string.concat(_socialKPIs.social, ": ");
UD60x18 contentKPIscore = ud(0);
// Calculate the content KPI score based on provided KPIs.
while (i < kpis.length) {
// Ensure the KPI kind exists within the campaign.
if (!registeredActionKind[string.concat(action, kpis[i].method)])
revert GIVEN_KPI_KIND_DOESNT_EXIST_ON_CAMPAIGN(
string.concat(action, kpis[i].method)
);
// Note: There is no explicit check to ensure KPIs are in the correct order.
// It is assumed they are provided in the correct order.
// Ensure that the provided KPI meets the required minimum threshold.
// If the submitted KPI value is lower than the predefined minimum,
// set `contentKPIscore` to zero and exit the loop as a no reward case.
if (_socialKPIs.kpis[i].min > kpis[i].min) {
contentKPIscore = ud(0);
break;
}
// Accumulate the KPI score based on predefined percentages and the KPI ratio.
contentKPIscore = contentKPIscore.add(
ud(uint256(_socialKPIs.kpis[i].pct) * 1e16).mul(
ud(kpis[i].min).div(ud(_socialKPIs.kpis[i].ratio))
)
);
unchecked {
i++;
}
}
if (i != kpis.length) return 0;
// Compute the final effective KPI score.
uint256 effectiveKPI = (contentQaScore.mul(contentKPIscore)).unwrap();
// Calculate the content KPI ratio relative to the total effective KPIs.
uint256 contentKPIratio = (effectiveKPI * 1e6) /
(_totalEffectiveKPIs + effectiveKPI);
// Determine the reward amount based on the reserved campaign budget.
uint256 rewardAmount = (_budgetInfo.reservedAmount * contentKPIratio) /
1e6;
// Ensure the reward does not exceed the maximum allowed per post.
if (
_budgetInfo.maxPerPost != 0 &&
rewardAmount >= _budgetInfo.maxPerPost
) {
rewardAmount = _budgetInfo.maxPerPost;
}
return rewardAmount;
}
/// @inheritdoc IEchoCampaign
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_
)
{
basicInfo_ = BasicInfo(
name(),
_ipfsCID,
owner(),
startTime(),
endTime()
);
initData_ = IEchoCampaignFactory(factory()).campaignNameToInitData(
name()
);
budgetInfo_ = _budgetInfo;
token_ = Token(
IERC20Metadata(_budgetInfo.token).name(),
IERC20Metadata(_budgetInfo.token).symbol(),
IERC20Metadata(_budgetInfo.token).decimals()
);
socialKPIs_ = _socialKPIs;
qaData_ = _qaData;
(uint256 lastPostTime, uint256 lastUpdatedTime) = IEchoContents(
contents()
).campaignContentInfo(address(this));
postInfo_ = PostInfo(
_totalPosts,
_totalEligibleContents,
_totalEffectiveKPIs,
lastPostTime,
lastUpdatedTime,
applicationFee()
);
status_ = currentStatus();
}
/// @inheritdoc IEchoCampaign
function getContentCreator(string calldata contentLink)
external
view
returns (address)
{
(bool contentRegistered, , , uint256 contentCreator) = IEchoContents(
contents()
).contentToCampaign(address(this), contentLink, "CONFIG: AuthorWallet");
if (!contentRegistered) revert INVALID_CONTENT_LINK();
return address(uint160(contentCreator));
}
/// @inheritdoc IEchoCampaign
function currentStatus() public view returns (Status) {
// If the status has been explicitly set to claimable or finalized, return it directly.
if (_status == Status.claimable || _status == Status.finalized)
return _status;
// If the current time is before the campaign start, mark it as upcoming.
if (block.timestamp <= startTime()) return Status.upcoming;
// If the campaign is active (between start and end time) and not finalized.
if (block.timestamp > startTime() && block.timestamp <= endTime())
if (_campaignPaused) return Status.paused;
else return Status.inProgress;
// If the current time is past the campaign end, mark it as finished.
return Status.finished;
}
/// @inheritdoc IEchoCampaign
function applicationFee() public view returns (uint256) {
// Get the total number of qualification data entries.
uint256 length = _qaData.length;
uint256 i;
uint256 totalFee;
uint256 tempFee;
string memory action;
// Iterate through each qualification data entry to compute its respective oracle fee.
while (i < length) {
// Determine the action type based on the QA method.
if (_qaData[i].method == QA_METHOD.AI) action = "QA-AI: ";
else if (_qaData[i].method == QA_METHOD.COMMUNITY)
action = "QA-C: ";
else action = "QA-V: ";
// Fetch the fee associated with the given QA kind from the administration contract.
(, , tempFee, ) = IEchoAdministration(admin()).modelData(
_qaData[i].oracle,
keccak256(abi.encodePacked(action, _qaData[i].kind))
);
if (tempFee == 0 || tempFee == 1) delete tempFee;
unchecked {
// Accumulate the fee.
totalFee += tempFee;
i++;
}
}
// Reset iteration variables for the next calculation.
delete i;
length = _socialKPIs.kpis.length;
action = string.concat(_socialKPIs.social, ": ");
// Iterate through each social KPI to compute its respective fee.
while (i < length) {
// Fetch the fee associated with the KPI from the administration contract.
(, , tempFee, ) = IEchoAdministration(admin()).modelData(
address(0),
keccak256(abi.encodePacked(action, _socialKPIs.kpis[i].method))
);
if (tempFee == 0 || tempFee == 1) delete tempFee;
unchecked {
// Accumulate the fee.
totalFee += tempFee;
i++;
}
}
// Return the total accumulated fee for both QA data and social KPIs.
return totalFee;
}
/// @inheritdoc IEchoCampaign
function name() public view returns (string memory) {
return
IEchoCampaignFactory(factory()).campaignNameHashToName(nameHash());
}
/// @inheritdoc IEchoCampaign
function contents() public view returns (address) {
return IEchoCampaignFactory(factory()).ECHO_CONTENTS();
}
/// @inheritdoc IEchoCampaign
function admin() public pure returns (address) {
return _getArgAddress(0);
}
/// @inheritdoc IEchoCampaign
function factory() public pure returns (address) {
return _getArgAddress(20);
}
/// @inheritdoc IEchoCampaign
function owner() public pure returns (address) {
return _getArgAddress(40);
}
/// @inheritdoc IEchoCampaign
function refundAddress() public pure returns (address) {
return _getArgAddress(60);
}
/// @inheritdoc IEchoCampaign
function nameHash() public pure returns (bytes32) {
return bytes32(_getArgUint256(80));
}
/// @inheritdoc IEchoCampaign
function startTime() public pure returns (uint256) {
return _getArgUint256(112);
}
/// @inheritdoc IEchoCampaign
function endTime() public pure returns (uint256) {
return _getArgUint256(144);
}
/*****************************\
|-*-*-*-* PRIVATE *-*-*-*-|
\*****************************/
function _refundPossibleAmountLeft() private returns (uint128) {
uint128 refundAmount;
_status = Status.claimable;
if (
_budgetInfo.maxPerPost != 0 &&
_totalEligibleContents * _budgetInfo.maxPerPost <
_budgetInfo.reservedAmount
) {
refundAmount = uint128(
_budgetInfo.reservedAmount -
(_totalEligibleContents * _budgetInfo.maxPerPost)
);
IERC20(_budgetInfo.token).transfer(refundAddress(), refundAmount);
_budgetInfo.reservedAmount -= refundAmount;
return refundAmount;
}
if (_budgetInfo.maxPerPost == 0 && _totalEligibleContents == 0) {
IERC20(_budgetInfo.token).transfer(
owner(),
_budgetInfo.reservedAmount
);
delete _budgetInfo.reservedAmount;
return refundAmount;
}
no_mid_refunding = true;
return refundAmount;
}
}// 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: BSD
pragma solidity 0.8.30;
/// @title Clone
/// @author zefram.eth
/// @notice Provides helper functions for reading immutable args from calldata
contract Clone {
/// @notice Reads an immutable arg with type address
/// @param argOffset The offset of the arg in the packed data
/// @return arg The arg value
function _getArgAddress(
uint256 argOffset
) internal pure returns (address arg) {
uint256 offset = _getImmutableArgsOffset();
// solhint-disable-next-line no-inline-assembly
assembly {
arg := shr(0x60, calldataload(add(offset, argOffset)))
}
}
/// @notice Reads an immutable arg with type uint256
/// @param argOffset The offset of the arg in the packed data
/// @return arg The arg value
function _getArgUint256(
uint256 argOffset
) internal pure returns (uint256 arg) {
uint256 offset = _getImmutableArgsOffset();
// solhint-disable-next-line no-inline-assembly
assembly {
arg := calldataload(add(offset, argOffset))
}
}
/// @notice Reads a uint256 array stored in the immutable args.
/// @param argOffset The offset of the arg in the packed data
/// @param arrLen Number of elements in the array
/// @return arr The array
function _getArgUint256Array(
uint256 argOffset,
uint64 arrLen
) internal pure returns (uint256[] memory arr) {
uint256 offset = _getImmutableArgsOffset();
uint256 el;
arr = new uint256[](arrLen);
for (uint64 i = 0; i < arrLen; i++) {
// solhint-disable-next-line no-inline-assembly
assembly {
el := calldataload(add(add(offset, argOffset), mul(i, 32)))
}
arr[i] = el;
}
return arr;
}
/// @notice Reads an immutable arg with type uint64
/// @param argOffset The offset of the arg in the packed data
/// @return arg The arg value
function _getArgUint64(
uint256 argOffset
) internal pure returns (uint64 arg) {
uint256 offset = _getImmutableArgsOffset();
// solhint-disable-next-line no-inline-assembly
assembly {
arg := shr(0xc0, calldataload(add(offset, argOffset)))
}
}
/// @notice Reads an immutable arg with type uint8
/// @param argOffset The offset of the arg in the packed data
/// @return arg The arg value
function _getArgUint8(uint256 argOffset) internal pure returns (uint8 arg) {
uint256 offset = _getImmutableArgsOffset();
// solhint-disable-next-line no-inline-assembly
assembly {
arg := shr(0xf8, calldataload(add(offset, argOffset)))
}
}
/// @return offset The offset of the packed immutable args in calldata
function _getImmutableArgsOffset() internal pure returns (uint256 offset) {
// solhint-disable-next-line no-inline-assembly
assembly {
offset := sub(
calldatasize(),
add(shr(240, calldataload(sub(calldatasize(), 2))), 2)
)
}
}
}// 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
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "./Casting.sol" as Casting;
import "./Helpers.sol" as Helpers;
import "./Math.sol" as Math;
/// @notice The unsigned 60.18-decimal fixed-point number representation, which can have up to 60 digits and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the Solidity type uint256.
/// @dev The value type is defined here so it can be imported in all other files.
type UD60x18 is uint256;
/*//////////////////////////////////////////////////////////////////////////
CASTING
//////////////////////////////////////////////////////////////////////////*/
using {
Casting.intoSD1x18,
Casting.intoUD2x18,
Casting.intoSD59x18,
Casting.intoUint128,
Casting.intoUint256,
Casting.intoUint40,
Casting.unwrap
} for UD60x18 global;
/*//////////////////////////////////////////////////////////////////////////
MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
// The global "using for" directive makes the functions in this library callable on the UD60x18 type.
using {
Math.avg,
Math.ceil,
Math.div,
Math.exp,
Math.exp2,
Math.floor,
Math.frac,
Math.gm,
Math.inv,
Math.ln,
Math.log10,
Math.log2,
Math.mul,
Math.pow,
Math.powu,
Math.sqrt
} for UD60x18 global;
/*//////////////////////////////////////////////////////////////////////////
HELPER FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
// The global "using for" directive makes the functions in this library callable on the UD60x18 type.
using {
Helpers.add,
Helpers.and,
Helpers.eq,
Helpers.gt,
Helpers.gte,
Helpers.isZero,
Helpers.lshift,
Helpers.lt,
Helpers.lte,
Helpers.mod,
Helpers.neq,
Helpers.not,
Helpers.or,
Helpers.rshift,
Helpers.sub,
Helpers.uncheckedAdd,
Helpers.uncheckedSub,
Helpers.xor
} for UD60x18 global;
/*//////////////////////////////////////////////////////////////////////////
OPERATORS
//////////////////////////////////////////////////////////////////////////*/
// The global "using for" directive makes it possible to use these operators on the UD60x18 type.
using {
Helpers.add as +,
Helpers.and2 as &,
Math.div as /,
Helpers.eq as ==,
Helpers.gt as >,
Helpers.gte as >=,
Helpers.lt as <,
Helpers.lte as <=,
Helpers.or as |,
Helpers.mod as %,
Math.mul as *,
Helpers.neq as !=,
Helpers.not as ~,
Helpers.sub as -,
Helpers.xor as ^
} for UD60x18 global;// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import { wrap } from "./Casting.sol";
import {
uEXP_MAX_INPUT,
uEXP2_MAX_INPUT,
uHALF_UNIT,
uLOG2_10,
uLOG2_E,
uMAX_UD60x18,
uMAX_WHOLE_UD60x18,
UNIT,
uUNIT,
uUNIT_SQUARED,
ZERO
} from "./Constants.sol";
import { UD60x18 } from "./ValueType.sol";
/*//////////////////////////////////////////////////////////////////////////
MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Calculates the arithmetic average of x and y using the following formula:
///
/// $$
/// avg(x, y) = (x & y) + ((xUint ^ yUint) / 2)
/// $$
///
/// In English, this is what this formula does:
///
/// 1. AND x and y.
/// 2. Calculate half of XOR x and y.
/// 3. Add the two results together.
///
/// This technique is known as SWAR, which stands for "SIMD within a register". You can read more about it here:
/// https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// @param x The first operand as a UD60x18 number.
/// @param y The second operand as a UD60x18 number.
/// @return result The arithmetic average as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function avg(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
uint256 yUint = y.unwrap();
unchecked {
result = wrap((xUint & yUint) + ((xUint ^ yUint) >> 1));
}
}
/// @notice Yields the smallest whole number greater than or equal to x.
///
/// @dev This is optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional
/// counterparts. See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x must be less than or equal to `MAX_WHOLE_UD60x18`.
///
/// @param x The UD60x18 number to ceil.
/// @param result The smallest whole number greater than or equal to x, as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function ceil(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
if (xUint > uMAX_WHOLE_UD60x18) {
revert Errors.PRBMath_UD60x18_Ceil_Overflow(x);
}
assembly ("memory-safe") {
// Equivalent to `x % UNIT`.
let remainder := mod(x, uUNIT)
// Equivalent to `UNIT - remainder`.
let delta := sub(uUNIT, remainder)
// Equivalent to `x + remainder > 0 ? delta : 0`.
result := add(x, mul(delta, gt(remainder, 0)))
}
}
/// @notice Divides two UD60x18 numbers, returning a new UD60x18 number.
///
/// @dev Uses {Common.mulDiv} to enable overflow-safe multiplication and division.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
///
/// @param x The numerator as a UD60x18 number.
/// @param y The denominator as a UD60x18 number.
/// @param result The quotient as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function div(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(Common.mulDiv(x.unwrap(), uUNIT, y.unwrap()));
}
/// @notice Calculates the natural exponent of x using the following formula:
///
/// $$
/// e^x = 2^{x * log_2{e}}
/// $$
///
/// @dev Requirements:
/// - x must be less than 133_084258667509499441.
///
/// @param x The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
// This check prevents values greater than 192e18 from being passed to {exp2}.
if (xUint > uEXP_MAX_INPUT) {
revert Errors.PRBMath_UD60x18_Exp_InputTooBig(x);
}
unchecked {
// Inline the fixed-point multiplication to save gas.
uint256 doubleUnitProduct = xUint * uLOG2_E;
result = exp2(wrap(doubleUnitProduct / uUNIT));
}
}
/// @notice Calculates the binary exponent of x using the binary fraction method.
///
/// @dev See https://ethereum.stackexchange.com/q/79903/24693
///
/// Requirements:
/// - x must be less than 192e18.
/// - The result must fit in UD60x18.
///
/// @param x The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp2(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
// Numbers greater than or equal to 192e18 don't fit in the 192.64-bit format.
if (xUint > uEXP2_MAX_INPUT) {
revert Errors.PRBMath_UD60x18_Exp2_InputTooBig(x);
}
// Convert x to the 192.64-bit fixed-point format.
uint256 x_192x64 = (xUint << 64) / uUNIT;
// Pass x to the {Common.exp2} function, which uses the 192.64-bit fixed-point number representation.
result = wrap(Common.exp2(x_192x64));
}
/// @notice Yields the greatest whole number less than or equal to x.
/// @dev Optimized for fractional value inputs, because every whole value has (1e18 - 1) fractional counterparts.
/// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
/// @param x The UD60x18 number to floor.
/// @param result The greatest whole number less than or equal to x, as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function floor(UD60x18 x) pure returns (UD60x18 result) {
assembly ("memory-safe") {
// Equivalent to `x % UNIT`.
let remainder := mod(x, uUNIT)
// Equivalent to `x - remainder > 0 ? remainder : 0)`.
result := sub(x, mul(remainder, gt(remainder, 0)))
}
}
/// @notice Yields the excess beyond the floor of x using the odd function definition.
/// @dev See https://en.wikipedia.org/wiki/Fractional_part.
/// @param x The UD60x18 number to get the fractional part of.
/// @param result The fractional part of x as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function frac(UD60x18 x) pure returns (UD60x18 result) {
assembly ("memory-safe") {
result := mod(x, uUNIT)
}
}
/// @notice Calculates the geometric mean of x and y, i.e. $\sqrt{x * y}$, rounding down.
///
/// @dev Requirements:
/// - x * y must fit in UD60x18.
///
/// @param x The first operand as a UD60x18 number.
/// @param y The second operand as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function gm(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
uint256 yUint = y.unwrap();
if (xUint == 0 || yUint == 0) {
return ZERO;
}
unchecked {
// Checking for overflow this way is faster than letting Solidity do it.
uint256 xyUint = xUint * yUint;
if (xyUint / xUint != yUint) {
revert Errors.PRBMath_UD60x18_Gm_Overflow(x, y);
}
// We don't need to multiply the result by `UNIT` here because the x*y product picked up a factor of `UNIT`
// during multiplication. See the comments in {Common.sqrt}.
result = wrap(Common.sqrt(xyUint));
}
}
/// @notice Calculates the inverse of x.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x must not be zero.
///
/// @param x The UD60x18 number for which to calculate the inverse.
/// @return result The inverse as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function inv(UD60x18 x) pure returns (UD60x18 result) {
unchecked {
result = wrap(uUNIT_SQUARED / x.unwrap());
}
}
/// @notice Calculates the natural logarithm of x using the following formula:
///
/// $$
/// ln{x} = log_2{x} / log_2{e}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
/// - The precision isn't sufficiently fine-grained to return exactly `UNIT` when the input is `E`.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The UD60x18 number for which to calculate the natural logarithm.
/// @return result The natural logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function ln(UD60x18 x) pure returns (UD60x18 result) {
unchecked {
// Inline the fixed-point multiplication to save gas. This is overflow-safe because the maximum value that
// {log2} can return is ~196_205294292027477728.
result = wrap(log2(x).unwrap() * uUNIT / uLOG2_E);
}
}
/// @notice Calculates the common logarithm of x using the following formula:
///
/// $$
/// log_{10}{x} = log_2{x} / log_2{10}
/// $$
///
/// However, if x is an exact power of ten, a hard coded value is returned.
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The UD60x18 number for which to calculate the common logarithm.
/// @return result The common logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function log10(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
if (xUint < uUNIT) {
revert Errors.PRBMath_UD60x18_Log_InputTooSmall(x);
}
// Note that the `mul` in this assembly block is the standard multiplication operation, not {UD60x18.mul}.
// prettier-ignore
assembly ("memory-safe") {
switch x
case 1 { result := mul(uUNIT, sub(0, 18)) }
case 10 { result := mul(uUNIT, sub(1, 18)) }
case 100 { result := mul(uUNIT, sub(2, 18)) }
case 1000 { result := mul(uUNIT, sub(3, 18)) }
case 10000 { result := mul(uUNIT, sub(4, 18)) }
case 100000 { result := mul(uUNIT, sub(5, 18)) }
case 1000000 { result := mul(uUNIT, sub(6, 18)) }
case 10000000 { result := mul(uUNIT, sub(7, 18)) }
case 100000000 { result := mul(uUNIT, sub(8, 18)) }
case 1000000000 { result := mul(uUNIT, sub(9, 18)) }
case 10000000000 { result := mul(uUNIT, sub(10, 18)) }
case 100000000000 { result := mul(uUNIT, sub(11, 18)) }
case 1000000000000 { result := mul(uUNIT, sub(12, 18)) }
case 10000000000000 { result := mul(uUNIT, sub(13, 18)) }
case 100000000000000 { result := mul(uUNIT, sub(14, 18)) }
case 1000000000000000 { result := mul(uUNIT, sub(15, 18)) }
case 10000000000000000 { result := mul(uUNIT, sub(16, 18)) }
case 100000000000000000 { result := mul(uUNIT, sub(17, 18)) }
case 1000000000000000000 { result := 0 }
case 10000000000000000000 { result := uUNIT }
case 100000000000000000000 { result := mul(uUNIT, 2) }
case 1000000000000000000000 { result := mul(uUNIT, 3) }
case 10000000000000000000000 { result := mul(uUNIT, 4) }
case 100000000000000000000000 { result := mul(uUNIT, 5) }
case 1000000000000000000000000 { result := mul(uUNIT, 6) }
case 10000000000000000000000000 { result := mul(uUNIT, 7) }
case 100000000000000000000000000 { result := mul(uUNIT, 8) }
case 1000000000000000000000000000 { result := mul(uUNIT, 9) }
case 10000000000000000000000000000 { result := mul(uUNIT, 10) }
case 100000000000000000000000000000 { result := mul(uUNIT, 11) }
case 1000000000000000000000000000000 { result := mul(uUNIT, 12) }
case 10000000000000000000000000000000 { result := mul(uUNIT, 13) }
case 100000000000000000000000000000000 { result := mul(uUNIT, 14) }
case 1000000000000000000000000000000000 { result := mul(uUNIT, 15) }
case 10000000000000000000000000000000000 { result := mul(uUNIT, 16) }
case 100000000000000000000000000000000000 { result := mul(uUNIT, 17) }
case 1000000000000000000000000000000000000 { result := mul(uUNIT, 18) }
case 10000000000000000000000000000000000000 { result := mul(uUNIT, 19) }
case 100000000000000000000000000000000000000 { result := mul(uUNIT, 20) }
case 1000000000000000000000000000000000000000 { result := mul(uUNIT, 21) }
case 10000000000000000000000000000000000000000 { result := mul(uUNIT, 22) }
case 100000000000000000000000000000000000000000 { result := mul(uUNIT, 23) }
case 1000000000000000000000000000000000000000000 { result := mul(uUNIT, 24) }
case 10000000000000000000000000000000000000000000 { result := mul(uUNIT, 25) }
case 100000000000000000000000000000000000000000000 { result := mul(uUNIT, 26) }
case 1000000000000000000000000000000000000000000000 { result := mul(uUNIT, 27) }
case 10000000000000000000000000000000000000000000000 { result := mul(uUNIT, 28) }
case 100000000000000000000000000000000000000000000000 { result := mul(uUNIT, 29) }
case 1000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 30) }
case 10000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 31) }
case 100000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 32) }
case 1000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 33) }
case 10000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 34) }
case 100000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 35) }
case 1000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 36) }
case 10000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 37) }
case 100000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 38) }
case 1000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 39) }
case 10000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 40) }
case 100000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 41) }
case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 42) }
case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 43) }
case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 44) }
case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 45) }
case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 46) }
case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 47) }
case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 48) }
case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 49) }
case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 50) }
case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 51) }
case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 52) }
case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 53) }
case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 54) }
case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 55) }
case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 56) }
case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 57) }
case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 58) }
case 100000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 59) }
default { result := uMAX_UD60x18 }
}
if (result.unwrap() == uMAX_UD60x18) {
unchecked {
// Inline the fixed-point division to save gas.
result = wrap(log2(x).unwrap() * uUNIT / uLOG2_10);
}
}
}
/// @notice Calculates the binary logarithm of x using the iterative approximation algorithm:
///
/// $$
/// log_2{x} = n + log_2{y}, \text{ where } y = x*2^{-n}, \ y \in [1, 2)
/// $$
///
/// For $0 \leq x \lt 1$, the input is inverted:
///
/// $$
/// log_2{x} = -log_2{\frac{1}{x}}
/// $$
///
/// @dev See https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
///
/// Notes:
/// - Due to the lossy precision of the iterative approximation, the results are not perfectly accurate to the last decimal.
///
/// Requirements:
/// - x must be greater than zero.
///
/// @param x The UD60x18 number for which to calculate the binary logarithm.
/// @return result The binary logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function log2(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
if (xUint < uUNIT) {
revert Errors.PRBMath_UD60x18_Log_InputTooSmall(x);
}
unchecked {
// Calculate the integer part of the logarithm.
uint256 n = Common.msb(xUint / uUNIT);
// This is the integer part of the logarithm as a UD60x18 number. The operation can't overflow because n
// n is at most 255 and UNIT is 1e18.
uint256 resultUint = n * uUNIT;
// Calculate $y = x * 2^{-n}$.
uint256 y = xUint >> n;
// If y is the unit number, the fractional part is zero.
if (y == uUNIT) {
return wrap(resultUint);
}
// Calculate the fractional part via the iterative approximation.
// The `delta >>= 1` part is equivalent to `delta /= 2`, but shifting bits is more gas efficient.
uint256 DOUBLE_UNIT = 2e18;
for (uint256 delta = uHALF_UNIT; delta > 0; delta >>= 1) {
y = (y * y) / uUNIT;
// Is y^2 >= 2e18 and so in the range [2e18, 4e18)?
if (y >= DOUBLE_UNIT) {
// Add the 2^{-m} factor to the logarithm.
resultUint += delta;
// Halve y, which corresponds to z/2 in the Wikipedia article.
y >>= 1;
}
}
result = wrap(resultUint);
}
}
/// @notice Multiplies two UD60x18 numbers together, returning a new UD60x18 number.
///
/// @dev Uses {Common.mulDiv} to enable overflow-safe multiplication and division.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
///
/// @dev See the documentation in {Common.mulDiv18}.
/// @param x The multiplicand as a UD60x18 number.
/// @param y The multiplier as a UD60x18 number.
/// @return result The product as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function mul(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(Common.mulDiv18(x.unwrap(), y.unwrap()));
}
/// @notice Raises x to the power of y.
///
/// For $1 \leq x \leq \infty$, the following standard formula is used:
///
/// $$
/// x^y = 2^{log_2{x} * y}
/// $$
///
/// For $0 \leq x \lt 1$, since the unsigned {log2} is undefined, an equivalent formula is used:
///
/// $$
/// i = \frac{1}{x}
/// w = 2^{log_2{i} * y}
/// x^y = \frac{1}{w}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2} and {mul}.
/// - Returns `UNIT` for 0^0.
/// - It may not perform well with very small values of x. Consider using SD59x18 as an alternative.
///
/// Requirements:
/// - Refer to the requirements in {exp2}, {log2}, and {mul}.
///
/// @param x The base as a UD60x18 number.
/// @param y The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function pow(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
uint256 yUint = y.unwrap();
// If both x and y are zero, the result is `UNIT`. If just x is zero, the result is always zero.
if (xUint == 0) {
return yUint == 0 ? UNIT : ZERO;
}
// If x is `UNIT`, the result is always `UNIT`.
else if (xUint == uUNIT) {
return UNIT;
}
// If y is zero, the result is always `UNIT`.
if (yUint == 0) {
return UNIT;
}
// If y is `UNIT`, the result is always x.
else if (yUint == uUNIT) {
return x;
}
// If x is greater than `UNIT`, use the standard formula.
if (xUint > uUNIT) {
result = exp2(mul(log2(x), y));
}
// Conversely, if x is less than `UNIT`, use the equivalent formula.
else {
UD60x18 i = wrap(uUNIT_SQUARED / xUint);
UD60x18 w = exp2(mul(log2(i), y));
result = wrap(uUNIT_SQUARED / w.unwrap());
}
}
/// @notice Raises x (a UD60x18 number) to the power y (an unsigned basic integer) using the well-known
/// algorithm "exponentiation by squaring".
///
/// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv18}.
/// - Returns `UNIT` for 0^0.
///
/// Requirements:
/// - The result must fit in UD60x18.
///
/// @param x The base as a UD60x18 number.
/// @param y The exponent as a uint256.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function powu(UD60x18 x, uint256 y) pure returns (UD60x18 result) {
// Calculate the first iteration of the loop in advance.
uint256 xUint = x.unwrap();
uint256 resultUint = y & 1 > 0 ? xUint : uUNIT;
// Equivalent to `for(y /= 2; y > 0; y /= 2)`.
for (y >>= 1; y > 0; y >>= 1) {
xUint = Common.mulDiv18(xUint, xUint);
// Equivalent to `y % 2 == 1`.
if (y & 1 > 0) {
resultUint = Common.mulDiv18(resultUint, xUint);
}
}
result = wrap(resultUint);
}
/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x must be less than `MAX_UD60x18 / UNIT`.
///
/// @param x The UD60x18 number for which to calculate the square root.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function sqrt(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
unchecked {
if (xUint > uMAX_UD60x18 / uUNIT) {
revert Errors.PRBMath_UD60x18_Sqrt_Overflow(x);
}
// Multiply x by `UNIT` to account for the factor of `UNIT` picked up when multiplying two UD60x18 numbers.
// In this case, the two numbers are both the square root.
result = wrap(Common.sqrt(xUint * uUNIT));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { wrap } from "./Casting.sol";
import { UD60x18 } from "./ValueType.sol";
/// @notice Implements the checked addition operation (+) in the UD60x18 type.
function add(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(x.unwrap() + y.unwrap());
}
/// @notice Implements the AND (&) bitwise operation in the UD60x18 type.
function and(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
result = wrap(x.unwrap() & bits);
}
/// @notice Implements the AND (&) bitwise operation in the UD60x18 type.
function and2(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(x.unwrap() & y.unwrap());
}
/// @notice Implements the equal operation (==) in the UD60x18 type.
function eq(UD60x18 x, UD60x18 y) pure returns (bool result) {
result = x.unwrap() == y.unwrap();
}
/// @notice Implements the greater than operation (>) in the UD60x18 type.
function gt(UD60x18 x, UD60x18 y) pure returns (bool result) {
result = x.unwrap() > y.unwrap();
}
/// @notice Implements the greater than or equal to operation (>=) in the UD60x18 type.
function gte(UD60x18 x, UD60x18 y) pure returns (bool result) {
result = x.unwrap() >= y.unwrap();
}
/// @notice Implements a zero comparison check function in the UD60x18 type.
function isZero(UD60x18 x) pure returns (bool result) {
// This wouldn't work if x could be negative.
result = x.unwrap() == 0;
}
/// @notice Implements the left shift operation (<<) in the UD60x18 type.
function lshift(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
result = wrap(x.unwrap() << bits);
}
/// @notice Implements the lower than operation (<) in the UD60x18 type.
function lt(UD60x18 x, UD60x18 y) pure returns (bool result) {
result = x.unwrap() < y.unwrap();
}
/// @notice Implements the lower than or equal to operation (<=) in the UD60x18 type.
function lte(UD60x18 x, UD60x18 y) pure returns (bool result) {
result = x.unwrap() <= y.unwrap();
}
/// @notice Implements the checked modulo operation (%) in the UD60x18 type.
function mod(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(x.unwrap() % y.unwrap());
}
/// @notice Implements the not equal operation (!=) in the UD60x18 type.
function neq(UD60x18 x, UD60x18 y) pure returns (bool result) {
result = x.unwrap() != y.unwrap();
}
/// @notice Implements the NOT (~) bitwise operation in the UD60x18 type.
function not(UD60x18 x) pure returns (UD60x18 result) {
result = wrap(~x.unwrap());
}
/// @notice Implements the OR (|) bitwise operation in the UD60x18 type.
function or(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(x.unwrap() | y.unwrap());
}
/// @notice Implements the right shift operation (>>) in the UD60x18 type.
function rshift(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
result = wrap(x.unwrap() >> bits);
}
/// @notice Implements the checked subtraction operation (-) in the UD60x18 type.
function sub(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(x.unwrap() - y.unwrap());
}
/// @notice Implements the unchecked addition operation (+) in the UD60x18 type.
function uncheckedAdd(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
unchecked {
result = wrap(x.unwrap() + y.unwrap());
}
}
/// @notice Implements the unchecked subtraction operation (-) in the UD60x18 type.
function uncheckedSub(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
unchecked {
result = wrap(x.unwrap() - y.unwrap());
}
}
/// @notice Implements the XOR (^) bitwise operation in the UD60x18 type.
function xor(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(x.unwrap() ^ y.unwrap());
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { UD60x18 } from "./ValueType.sol";
/// @notice Thrown when ceiling a number overflows UD60x18.
error PRBMath_UD60x18_Ceil_Overflow(UD60x18 x);
/// @notice Thrown when converting a basic integer to the fixed-point format overflows UD60x18.
error PRBMath_UD60x18_Convert_Overflow(uint256 x);
/// @notice Thrown when taking the natural exponent of a base greater than 133_084258667509499441.
error PRBMath_UD60x18_Exp_InputTooBig(UD60x18 x);
/// @notice Thrown when taking the binary exponent of a base greater than 192e18.
error PRBMath_UD60x18_Exp2_InputTooBig(UD60x18 x);
/// @notice Thrown when taking the geometric mean of two numbers and multiplying them overflows UD60x18.
error PRBMath_UD60x18_Gm_Overflow(UD60x18 x, UD60x18 y);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18.
error PRBMath_UD60x18_IntoSD1x18_Overflow(UD60x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD59x18.
error PRBMath_UD60x18_IntoSD59x18_Overflow(UD60x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18.
error PRBMath_UD60x18_IntoUD2x18_Overflow(UD60x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128.
error PRBMath_UD60x18_IntoUint128_Overflow(UD60x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint40.
error PRBMath_UD60x18_IntoUint40_Overflow(UD60x18 x);
/// @notice Thrown when taking the logarithm of a number less than 1.
error PRBMath_UD60x18_Log_InputTooSmall(UD60x18 x);
/// @notice Thrown when calculating the square root overflows UD60x18.
error PRBMath_UD60x18_Sqrt_Overflow(UD60x18 x);// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { uMAX_UD60x18, uUNIT } from "./Constants.sol";
import { PRBMath_UD60x18_Convert_Overflow } from "./Errors.sol";
import { UD60x18 } from "./ValueType.sol";
/// @notice Converts a UD60x18 number to a simple integer by dividing it by `UNIT`.
/// @dev The result is rounded toward zero.
/// @param x The UD60x18 number to convert.
/// @return result The same number in basic integer form.
function convert(UD60x18 x) pure returns (uint256 result) {
result = UD60x18.unwrap(x) / uUNIT;
}
/// @notice Converts a simple integer to UD60x18 by multiplying it by `UNIT`.
///
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UD60x18 / UNIT`.
///
/// @param x The basic integer to convert.
/// @param result The same number converted to UD60x18.
function convert(uint256 x) pure returns (UD60x18 result) {
if (x > uMAX_UD60x18 / uUNIT) {
revert PRBMath_UD60x18_Convert_Overflow(x);
}
unchecked {
result = UD60x18.wrap(x * uUNIT);
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { UD60x18 } from "./ValueType.sol";
// NOTICE: the "u" prefix stands for "unwrapped".
/// @dev Euler's number as a UD60x18 number.
UD60x18 constant E = UD60x18.wrap(2_718281828459045235);
/// @dev The maximum input permitted in {exp}.
uint256 constant uEXP_MAX_INPUT = 133_084258667509499440;
UD60x18 constant EXP_MAX_INPUT = UD60x18.wrap(uEXP_MAX_INPUT);
/// @dev The maximum input permitted in {exp2}.
uint256 constant uEXP2_MAX_INPUT = 192e18 - 1;
UD60x18 constant EXP2_MAX_INPUT = UD60x18.wrap(uEXP2_MAX_INPUT);
/// @dev Half the UNIT number.
uint256 constant uHALF_UNIT = 0.5e18;
UD60x18 constant HALF_UNIT = UD60x18.wrap(uHALF_UNIT);
/// @dev $log_2(10)$ as a UD60x18 number.
uint256 constant uLOG2_10 = 3_321928094887362347;
UD60x18 constant LOG2_10 = UD60x18.wrap(uLOG2_10);
/// @dev $log_2(e)$ as a UD60x18 number.
uint256 constant uLOG2_E = 1_442695040888963407;
UD60x18 constant LOG2_E = UD60x18.wrap(uLOG2_E);
/// @dev The maximum value a UD60x18 number can have.
uint256 constant uMAX_UD60x18 = 115792089237316195423570985008687907853269984665640564039457_584007913129639935;
UD60x18 constant MAX_UD60x18 = UD60x18.wrap(uMAX_UD60x18);
/// @dev The maximum whole value a UD60x18 number can have.
uint256 constant uMAX_WHOLE_UD60x18 = 115792089237316195423570985008687907853269984665640564039457_000000000000000000;
UD60x18 constant MAX_WHOLE_UD60x18 = UD60x18.wrap(uMAX_WHOLE_UD60x18);
/// @dev PI as a UD60x18 number.
UD60x18 constant PI = UD60x18.wrap(3_141592653589793238);
/// @dev The unit number, which gives the decimal precision of UD60x18.
uint256 constant uUNIT = 1e18;
UD60x18 constant UNIT = UD60x18.wrap(uUNIT);
/// @dev The unit number squared.
uint256 constant uUNIT_SQUARED = 1e36;
UD60x18 constant UNIT_SQUARED = UD60x18.wrap(uUNIT_SQUARED);
/// @dev Zero as a UD60x18 number.
UD60x18 constant ZERO = UD60x18.wrap(0);// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "./Errors.sol" as CastingErrors;
import { MAX_UINT128, MAX_UINT40 } from "../Common.sol";
import { uMAX_SD1x18 } from "../sd1x18/Constants.sol";
import { SD1x18 } from "../sd1x18/ValueType.sol";
import { uMAX_SD59x18 } from "../sd59x18/Constants.sol";
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { uMAX_UD2x18 } from "../ud2x18/Constants.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD60x18 } from "./ValueType.sol";
/// @notice Casts a UD60x18 number into SD1x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_SD1x18`.
function intoSD1x18(UD60x18 x) pure returns (SD1x18 result) {
uint256 xUint = UD60x18.unwrap(x);
if (xUint > uint256(int256(uMAX_SD1x18))) {
revert CastingErrors.PRBMath_UD60x18_IntoSD1x18_Overflow(x);
}
result = SD1x18.wrap(int64(uint64(xUint)));
}
/// @notice Casts a UD60x18 number into UD2x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_UD2x18`.
function intoUD2x18(UD60x18 x) pure returns (UD2x18 result) {
uint256 xUint = UD60x18.unwrap(x);
if (xUint > uMAX_UD2x18) {
revert CastingErrors.PRBMath_UD60x18_IntoUD2x18_Overflow(x);
}
result = UD2x18.wrap(uint64(xUint));
}
/// @notice Casts a UD60x18 number into SD59x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_SD59x18`.
function intoSD59x18(UD60x18 x) pure returns (SD59x18 result) {
uint256 xUint = UD60x18.unwrap(x);
if (xUint > uint256(uMAX_SD59x18)) {
revert CastingErrors.PRBMath_UD60x18_IntoSD59x18_Overflow(x);
}
result = SD59x18.wrap(int256(xUint));
}
/// @notice Casts a UD60x18 number into uint128.
/// @dev This is basically an alias for {unwrap}.
function intoUint256(UD60x18 x) pure returns (uint256 result) {
result = UD60x18.unwrap(x);
}
/// @notice Casts a UD60x18 number into uint128.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UINT128`.
function intoUint128(UD60x18 x) pure returns (uint128 result) {
uint256 xUint = UD60x18.unwrap(x);
if (xUint > MAX_UINT128) {
revert CastingErrors.PRBMath_UD60x18_IntoUint128_Overflow(x);
}
result = uint128(xUint);
}
/// @notice Casts a UD60x18 number into uint40.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(UD60x18 x) pure returns (uint40 result) {
uint256 xUint = UD60x18.unwrap(x);
if (xUint > MAX_UINT40) {
revert CastingErrors.PRBMath_UD60x18_IntoUint40_Overflow(x);
}
result = uint40(xUint);
}
/// @notice Alias for {wrap}.
function ud(uint256 x) pure returns (UD60x18 result) {
result = UD60x18.wrap(x);
}
/// @notice Alias for {wrap}.
function ud60x18(uint256 x) pure returns (UD60x18 result) {
result = UD60x18.wrap(x);
}
/// @notice Unwraps a UD60x18 number into uint256.
function unwrap(UD60x18 x) pure returns (uint256 result) {
result = UD60x18.unwrap(x);
}
/// @notice Wraps a uint256 number into the UD60x18 value type.
function wrap(uint256 x) pure returns (UD60x18 result) {
result = UD60x18.wrap(x);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
// Common.sol
//
// Common mathematical functions used in both SD59x18 and UD60x18. Note that these global functions do not
// always operate with SD59x18 and UD60x18 numbers.
/*//////////////////////////////////////////////////////////////////////////
CUSTOM ERRORS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when the resultant value in {mulDiv} overflows uint256.
error PRBMath_MulDiv_Overflow(uint256 x, uint256 y, uint256 denominator);
/// @notice Thrown when the resultant value in {mulDiv18} overflows uint256.
error PRBMath_MulDiv18_Overflow(uint256 x, uint256 y);
/// @notice Thrown when one of the inputs passed to {mulDivSigned} is `type(int256).min`.
error PRBMath_MulDivSigned_InputTooSmall();
/// @notice Thrown when the resultant value in {mulDivSigned} overflows int256.
error PRBMath_MulDivSigned_Overflow(int256 x, int256 y);
/*//////////////////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////////////////*/
/// @dev The maximum value a uint128 number can have.
uint128 constant MAX_UINT128 = type(uint128).max;
/// @dev The maximum value a uint40 number can have.
uint40 constant MAX_UINT40 = type(uint40).max;
/// @dev The unit number, which the decimal precision of the fixed-point types.
uint256 constant UNIT = 1e18;
/// @dev The unit number inverted mod 2^256.
uint256 constant UNIT_INVERSE = 78156646155174841979727994598816262306175212592076161876661_508869554232690281;
/// @dev The the largest power of two that divides the decimal value of `UNIT`. The logarithm of this value is the least significant
/// bit in the binary representation of `UNIT`.
uint256 constant UNIT_LPOTD = 262144;
/*//////////////////////////////////////////////////////////////////////////
FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Calculates the binary exponent of x using the binary fraction method.
/// @dev Has to use 192.64-bit fixed-point numbers. See https://ethereum.stackexchange.com/a/96594/24693.
/// @param x The exponent as an unsigned 192.64-bit fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function exp2(uint256 x) pure returns (uint256 result) {
unchecked {
// Start from 0.5 in the 192.64-bit fixed-point format.
result = 0x800000000000000000000000000000000000000000000000;
// The following logic multiplies the result by $\sqrt{2^{-i}}$ when the bit at position i is 1. Key points:
//
// 1. Intermediate results will not overflow, as the starting point is 2^191 and all magic factors are under 2^65.
// 2. The rationale for organizing the if statements into groups of 8 is gas savings. If the result of performing
// a bitwise AND operation between x and any value in the array [0x80; 0x40; 0x20; 0x10; 0x08; 0x04; 0x02; 0x01] is 1,
// we know that `x & 0xFF` is also 1.
if (x & 0xFF00000000000000 > 0) {
if (x & 0x8000000000000000 > 0) {
result = (result * 0x16A09E667F3BCC909) >> 64;
}
if (x & 0x4000000000000000 > 0) {
result = (result * 0x1306FE0A31B7152DF) >> 64;
}
if (x & 0x2000000000000000 > 0) {
result = (result * 0x1172B83C7D517ADCE) >> 64;
}
if (x & 0x1000000000000000 > 0) {
result = (result * 0x10B5586CF9890F62A) >> 64;
}
if (x & 0x800000000000000 > 0) {
result = (result * 0x1059B0D31585743AE) >> 64;
}
if (x & 0x400000000000000 > 0) {
result = (result * 0x102C9A3E778060EE7) >> 64;
}
if (x & 0x200000000000000 > 0) {
result = (result * 0x10163DA9FB33356D8) >> 64;
}
if (x & 0x100000000000000 > 0) {
result = (result * 0x100B1AFA5ABCBED61) >> 64;
}
}
if (x & 0xFF000000000000 > 0) {
if (x & 0x80000000000000 > 0) {
result = (result * 0x10058C86DA1C09EA2) >> 64;
}
if (x & 0x40000000000000 > 0) {
result = (result * 0x1002C605E2E8CEC50) >> 64;
}
if (x & 0x20000000000000 > 0) {
result = (result * 0x100162F3904051FA1) >> 64;
}
if (x & 0x10000000000000 > 0) {
result = (result * 0x1000B175EFFDC76BA) >> 64;
}
if (x & 0x8000000000000 > 0) {
result = (result * 0x100058BA01FB9F96D) >> 64;
}
if (x & 0x4000000000000 > 0) {
result = (result * 0x10002C5CC37DA9492) >> 64;
}
if (x & 0x2000000000000 > 0) {
result = (result * 0x1000162E525EE0547) >> 64;
}
if (x & 0x1000000000000 > 0) {
result = (result * 0x10000B17255775C04) >> 64;
}
}
if (x & 0xFF0000000000 > 0) {
if (x & 0x800000000000 > 0) {
result = (result * 0x1000058B91B5BC9AE) >> 64;
}
if (x & 0x400000000000 > 0) {
result = (result * 0x100002C5C89D5EC6D) >> 64;
}
if (x & 0x200000000000 > 0) {
result = (result * 0x10000162E43F4F831) >> 64;
}
if (x & 0x100000000000 > 0) {
result = (result * 0x100000B1721BCFC9A) >> 64;
}
if (x & 0x80000000000 > 0) {
result = (result * 0x10000058B90CF1E6E) >> 64;
}
if (x & 0x40000000000 > 0) {
result = (result * 0x1000002C5C863B73F) >> 64;
}
if (x & 0x20000000000 > 0) {
result = (result * 0x100000162E430E5A2) >> 64;
}
if (x & 0x10000000000 > 0) {
result = (result * 0x1000000B172183551) >> 64;
}
}
if (x & 0xFF00000000 > 0) {
if (x & 0x8000000000 > 0) {
result = (result * 0x100000058B90C0B49) >> 64;
}
if (x & 0x4000000000 > 0) {
result = (result * 0x10000002C5C8601CC) >> 64;
}
if (x & 0x2000000000 > 0) {
result = (result * 0x1000000162E42FFF0) >> 64;
}
if (x & 0x1000000000 > 0) {
result = (result * 0x10000000B17217FBB) >> 64;
}
if (x & 0x800000000 > 0) {
result = (result * 0x1000000058B90BFCE) >> 64;
}
if (x & 0x400000000 > 0) {
result = (result * 0x100000002C5C85FE3) >> 64;
}
if (x & 0x200000000 > 0) {
result = (result * 0x10000000162E42FF1) >> 64;
}
if (x & 0x100000000 > 0) {
result = (result * 0x100000000B17217F8) >> 64;
}
}
if (x & 0xFF000000 > 0) {
if (x & 0x80000000 > 0) {
result = (result * 0x10000000058B90BFC) >> 64;
}
if (x & 0x40000000 > 0) {
result = (result * 0x1000000002C5C85FE) >> 64;
}
if (x & 0x20000000 > 0) {
result = (result * 0x100000000162E42FF) >> 64;
}
if (x & 0x10000000 > 0) {
result = (result * 0x1000000000B17217F) >> 64;
}
if (x & 0x8000000 > 0) {
result = (result * 0x100000000058B90C0) >> 64;
}
if (x & 0x4000000 > 0) {
result = (result * 0x10000000002C5C860) >> 64;
}
if (x & 0x2000000 > 0) {
result = (result * 0x1000000000162E430) >> 64;
}
if (x & 0x1000000 > 0) {
result = (result * 0x10000000000B17218) >> 64;
}
}
if (x & 0xFF0000 > 0) {
if (x & 0x800000 > 0) {
result = (result * 0x1000000000058B90C) >> 64;
}
if (x & 0x400000 > 0) {
result = (result * 0x100000000002C5C86) >> 64;
}
if (x & 0x200000 > 0) {
result = (result * 0x10000000000162E43) >> 64;
}
if (x & 0x100000 > 0) {
result = (result * 0x100000000000B1721) >> 64;
}
if (x & 0x80000 > 0) {
result = (result * 0x10000000000058B91) >> 64;
}
if (x & 0x40000 > 0) {
result = (result * 0x1000000000002C5C8) >> 64;
}
if (x & 0x20000 > 0) {
result = (result * 0x100000000000162E4) >> 64;
}
if (x & 0x10000 > 0) {
result = (result * 0x1000000000000B172) >> 64;
}
}
if (x & 0xFF00 > 0) {
if (x & 0x8000 > 0) {
result = (result * 0x100000000000058B9) >> 64;
}
if (x & 0x4000 > 0) {
result = (result * 0x10000000000002C5D) >> 64;
}
if (x & 0x2000 > 0) {
result = (result * 0x1000000000000162E) >> 64;
}
if (x & 0x1000 > 0) {
result = (result * 0x10000000000000B17) >> 64;
}
if (x & 0x800 > 0) {
result = (result * 0x1000000000000058C) >> 64;
}
if (x & 0x400 > 0) {
result = (result * 0x100000000000002C6) >> 64;
}
if (x & 0x200 > 0) {
result = (result * 0x10000000000000163) >> 64;
}
if (x & 0x100 > 0) {
result = (result * 0x100000000000000B1) >> 64;
}
}
if (x & 0xFF > 0) {
if (x & 0x80 > 0) {
result = (result * 0x10000000000000059) >> 64;
}
if (x & 0x40 > 0) {
result = (result * 0x1000000000000002C) >> 64;
}
if (x & 0x20 > 0) {
result = (result * 0x10000000000000016) >> 64;
}
if (x & 0x10 > 0) {
result = (result * 0x1000000000000000B) >> 64;
}
if (x & 0x8 > 0) {
result = (result * 0x10000000000000006) >> 64;
}
if (x & 0x4 > 0) {
result = (result * 0x10000000000000003) >> 64;
}
if (x & 0x2 > 0) {
result = (result * 0x10000000000000001) >> 64;
}
if (x & 0x1 > 0) {
result = (result * 0x10000000000000001) >> 64;
}
}
// In the code snippet below, two operations are executed simultaneously:
//
// 1. The result is multiplied by $(2^n + 1)$, where $2^n$ represents the integer part, and the additional 1
// accounts for the initial guess of 0.5. This is achieved by subtracting from 191 instead of 192.
// 2. The result is then converted to an unsigned 60.18-decimal fixed-point format.
//
// The underlying logic is based on the relationship $2^{191-ip} = 2^{ip} / 2^{191}$, where $ip$ denotes the,
// integer part, $2^n$.
result *= UNIT;
result >>= (191 - (x >> 64));
}
}
/// @notice Finds the zero-based index of the first 1 in the binary representation of x.
///
/// @dev See the note on "msb" in this Wikipedia article: https://en.wikipedia.org/wiki/Find_first_set
///
/// Each step in this implementation is equivalent to this high-level code:
///
/// ```solidity
/// if (x >= 2 ** 128) {
/// x >>= 128;
/// result += 128;
/// }
/// ```
///
/// Where 128 is replaced with each respective power of two factor. See the full high-level implementation here:
/// https://gist.github.com/PaulRBerg/f932f8693f2733e30c4d479e8e980948
///
/// The Yul instructions used below are:
///
/// - "gt" is "greater than"
/// - "or" is the OR bitwise operator
/// - "shl" is "shift left"
/// - "shr" is "shift right"
///
/// @param x The uint256 number for which to find the index of the most significant bit.
/// @return result The index of the most significant bit as a uint256.
/// @custom:smtchecker abstract-function-nondet
function msb(uint256 x) pure returns (uint256 result) {
// 2^128
assembly ("memory-safe") {
let factor := shl(7, gt(x, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^64
assembly ("memory-safe") {
let factor := shl(6, gt(x, 0xFFFFFFFFFFFFFFFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^32
assembly ("memory-safe") {
let factor := shl(5, gt(x, 0xFFFFFFFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^16
assembly ("memory-safe") {
let factor := shl(4, gt(x, 0xFFFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^8
assembly ("memory-safe") {
let factor := shl(3, gt(x, 0xFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^4
assembly ("memory-safe") {
let factor := shl(2, gt(x, 0xF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^2
assembly ("memory-safe") {
let factor := shl(1, gt(x, 0x3))
x := shr(factor, x)
result := or(result, factor)
}
// 2^1
// No need to shift x any more.
assembly ("memory-safe") {
let factor := gt(x, 0x1)
result := or(result, factor)
}
}
/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev Credits to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - The denominator must not be zero.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as a uint256.
/// @param y The multiplier as a uint256.
/// @param denominator The divisor as a uint256.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function mulDiv(uint256 x, uint256 y, uint256 denominator) pure returns (uint256 result) {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512-bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly ("memory-safe") {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
unchecked {
return prod0 / denominator;
}
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (prod1 >= denominator) {
revert PRBMath_MulDiv_Overflow(x, y, denominator);
}
////////////////////////////////////////////////////////////////////////////
// 512 by 256 division
////////////////////////////////////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly ("memory-safe") {
// Compute remainder using the mulmod Yul instruction.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512-bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
unchecked {
// Calculate the largest power of two divisor of the denominator using the unary operator ~. This operation cannot overflow
// because the denominator cannot be zero at this point in the function execution. The result is always >= 1.
// For more detail, see https://cs.stackexchange.com/q/138556/92363.
uint256 lpotdod = denominator & (~denominator + 1);
uint256 flippedLpotdod;
assembly ("memory-safe") {
// Factor powers of two out of denominator.
denominator := div(denominator, lpotdod)
// Divide [prod1 prod0] by lpotdod.
prod0 := div(prod0, lpotdod)
// Get the flipped value `2^256 / lpotdod`. If the `lpotdod` is zero, the flipped value is one.
// `sub(0, lpotdod)` produces the two's complement version of `lpotdod`, which is equivalent to flipping all the bits.
// However, `div` interprets this value as an unsigned value: https://ethereum.stackexchange.com/q/147168/24693
flippedLpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * flippedLpotdod;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
}
}
/// @notice Calculates x*y÷1e18 with 512-bit precision.
///
/// @dev A variant of {mulDiv} with constant folding, i.e. in which the denominator is hard coded to 1e18.
///
/// Notes:
/// - The body is purposely left uncommented; to understand how this works, see the documentation in {mulDiv}.
/// - The result is rounded toward zero.
/// - We take as an axiom that the result cannot be `MAX_UINT256` when x and y solve the following system of equations:
///
/// $$
/// \begin{cases}
/// x * y = MAX\_UINT256 * UNIT \\
/// (x * y) \% UNIT \geq \frac{UNIT}{2}
/// \end{cases}
/// $$
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
/// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function mulDiv18(uint256 x, uint256 y) pure returns (uint256 result) {
uint256 prod0;
uint256 prod1;
assembly ("memory-safe") {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
unchecked {
return prod0 / UNIT;
}
}
if (prod1 >= UNIT) {
revert PRBMath_MulDiv18_Overflow(x, y);
}
uint256 remainder;
assembly ("memory-safe") {
remainder := mulmod(x, y, UNIT)
result :=
mul(
or(
div(sub(prod0, remainder), UNIT_LPOTD),
mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, UNIT_LPOTD), UNIT_LPOTD), 1))
),
UNIT_INVERSE
)
}
}
/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev This is an extension of {mulDiv} for signed numbers, which works by computing the signs and the absolute values separately.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - None of the inputs can be `type(int256).min`.
/// - The result must fit in int256.
///
/// @param x The multiplicand as an int256.
/// @param y The multiplier as an int256.
/// @param denominator The divisor as an int256.
/// @return result The result as an int256.
/// @custom:smtchecker abstract-function-nondet
function mulDivSigned(int256 x, int256 y, int256 denominator) pure returns (int256 result) {
if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
revert PRBMath_MulDivSigned_InputTooSmall();
}
// Get hold of the absolute values of x, y and the denominator.
uint256 xAbs;
uint256 yAbs;
uint256 dAbs;
unchecked {
xAbs = x < 0 ? uint256(-x) : uint256(x);
yAbs = y < 0 ? uint256(-y) : uint256(y);
dAbs = denominator < 0 ? uint256(-denominator) : uint256(denominator);
}
// Compute the absolute value of x*y÷denominator. The result must fit in int256.
uint256 resultAbs = mulDiv(xAbs, yAbs, dAbs);
if (resultAbs > uint256(type(int256).max)) {
revert PRBMath_MulDivSigned_Overflow(x, y);
}
// Get the signs of x, y and the denominator.
uint256 sx;
uint256 sy;
uint256 sd;
assembly ("memory-safe") {
// "sgt" is the "signed greater than" assembly instruction and "sub(0,1)" is -1 in two's complement.
sx := sgt(x, sub(0, 1))
sy := sgt(y, sub(0, 1))
sd := sgt(denominator, sub(0, 1))
}
// XOR over sx, sy and sd. What this does is to check whether there are 1 or 3 negative signs in the inputs.
// If there are, the result should be negative. Otherwise, it should be positive.
unchecked {
result = sx ^ sy ^ sd == 0 ? -int256(resultAbs) : int256(resultAbs);
}
}
/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - If x is not a perfect square, the result is rounded down.
/// - Credits to OpenZeppelin for the explanations in comments below.
///
/// @param x The uint256 number for which to calculate the square root.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function sqrt(uint256 x) pure returns (uint256 result) {
if (x == 0) {
return 0;
}
// For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.
//
// We know that the "msb" (most significant bit) of x is a power of 2 such that we have:
//
// $$
// msb(x) <= x <= 2*msb(x)$
// $$
//
// We write $msb(x)$ as $2^k$, and we get:
//
// $$
// k = log_2(x)
// $$
//
// Thus, we can write the initial inequality as:
//
// $$
// 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\
// sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\
// 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}
// $$
//
// Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.
uint256 xAux = uint256(x);
result = 1;
if (xAux >= 2 ** 128) {
xAux >>= 128;
result <<= 64;
}
if (xAux >= 2 ** 64) {
xAux >>= 64;
result <<= 32;
}
if (xAux >= 2 ** 32) {
xAux >>= 32;
result <<= 16;
}
if (xAux >= 2 ** 16) {
xAux >>= 16;
result <<= 8;
}
if (xAux >= 2 ** 8) {
xAux >>= 8;
result <<= 4;
}
if (xAux >= 2 ** 4) {
xAux >>= 4;
result <<= 2;
}
if (xAux >= 2 ** 2) {
result <<= 1;
}
// At this point, `result` is an estimation with at least one bit of precision. We know the true value has at
// most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision
// doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of
// precision into the expected uint128 result.
unchecked {
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
// If x is not a perfect square, round the result toward zero.
uint256 roundedResult = x / result;
if (result >= roundedResult) {
result = roundedResult;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "./Casting.sol" as Casting;
/// @notice The unsigned 2.18-decimal fixed-point number representation, which can have up to 2 digits and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity
/// type uint64. This is useful when end users want to use uint64 to save gas, e.g. with tight variable packing in contract
/// storage.
type UD2x18 is uint64;
/*//////////////////////////////////////////////////////////////////////////
CASTING
//////////////////////////////////////////////////////////////////////////*/
using {
Casting.intoSD1x18,
Casting.intoSD59x18,
Casting.intoUD60x18,
Casting.intoUint256,
Casting.intoUint128,
Casting.intoUint40,
Casting.unwrap
} for UD2x18 global;// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { UD2x18 } from "./ValueType.sol";
/// @dev Euler's number as a UD2x18 number.
UD2x18 constant E = UD2x18.wrap(2_718281828459045235);
/// @dev The maximum value a UD2x18 number can have.
uint64 constant uMAX_UD2x18 = 18_446744073709551615;
UD2x18 constant MAX_UD2x18 = UD2x18.wrap(uMAX_UD2x18);
/// @dev PI as a UD2x18 number.
UD2x18 constant PI = UD2x18.wrap(3_141592653589793238);
/// @dev The unit number, which gives the decimal precision of UD2x18.
UD2x18 constant UNIT = UD2x18.wrap(1e18);
uint64 constant uUNIT = 1e18;// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "./Casting.sol" as Casting;
import "./Helpers.sol" as Helpers;
import "./Math.sol" as Math;
/// @notice The signed 59.18-decimal fixed-point number representation, which can have up to 59 digits and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity
/// type int256.
type SD59x18 is int256;
/*//////////////////////////////////////////////////////////////////////////
CASTING
//////////////////////////////////////////////////////////////////////////*/
using {
Casting.intoInt256,
Casting.intoSD1x18,
Casting.intoUD2x18,
Casting.intoUD60x18,
Casting.intoUint256,
Casting.intoUint128,
Casting.intoUint40,
Casting.unwrap
} for SD59x18 global;
/*//////////////////////////////////////////////////////////////////////////
MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
using {
Math.abs,
Math.avg,
Math.ceil,
Math.div,
Math.exp,
Math.exp2,
Math.floor,
Math.frac,
Math.gm,
Math.inv,
Math.log10,
Math.log2,
Math.ln,
Math.mul,
Math.pow,
Math.powu,
Math.sqrt
} for SD59x18 global;
/*//////////////////////////////////////////////////////////////////////////
HELPER FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
using {
Helpers.add,
Helpers.and,
Helpers.eq,
Helpers.gt,
Helpers.gte,
Helpers.isZero,
Helpers.lshift,
Helpers.lt,
Helpers.lte,
Helpers.mod,
Helpers.neq,
Helpers.not,
Helpers.or,
Helpers.rshift,
Helpers.sub,
Helpers.uncheckedAdd,
Helpers.uncheckedSub,
Helpers.uncheckedUnary,
Helpers.xor
} for SD59x18 global;
/*//////////////////////////////////////////////////////////////////////////
OPERATORS
//////////////////////////////////////////////////////////////////////////*/
// The global "using for" directive makes it possible to use these operators on the SD59x18 type.
using {
Helpers.add as +,
Helpers.and2 as &,
Math.div as /,
Helpers.eq as ==,
Helpers.gt as >,
Helpers.gte as >=,
Helpers.lt as <,
Helpers.lte as <=,
Helpers.mod as %,
Math.mul as *,
Helpers.neq as !=,
Helpers.not as ~,
Helpers.or as |,
Helpers.sub as -,
Helpers.unary as -,
Helpers.xor as ^
} for SD59x18 global;// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { SD59x18 } from "./ValueType.sol";
// NOTICE: the "u" prefix stands for "unwrapped".
/// @dev Euler's number as an SD59x18 number.
SD59x18 constant E = SD59x18.wrap(2_718281828459045235);
/// @dev The maximum input permitted in {exp}.
int256 constant uEXP_MAX_INPUT = 133_084258667509499440;
SD59x18 constant EXP_MAX_INPUT = SD59x18.wrap(uEXP_MAX_INPUT);
/// @dev Any value less than this returns 0 in {exp}.
int256 constant uEXP_MIN_THRESHOLD = -41_446531673892822322;
SD59x18 constant EXP_MIN_THRESHOLD = SD59x18.wrap(uEXP_MIN_THRESHOLD);
/// @dev The maximum input permitted in {exp2}.
int256 constant uEXP2_MAX_INPUT = 192e18 - 1;
SD59x18 constant EXP2_MAX_INPUT = SD59x18.wrap(uEXP2_MAX_INPUT);
/// @dev Any value less than this returns 0 in {exp2}.
int256 constant uEXP2_MIN_THRESHOLD = -59_794705707972522261;
SD59x18 constant EXP2_MIN_THRESHOLD = SD59x18.wrap(uEXP2_MIN_THRESHOLD);
/// @dev Half the UNIT number.
int256 constant uHALF_UNIT = 0.5e18;
SD59x18 constant HALF_UNIT = SD59x18.wrap(uHALF_UNIT);
/// @dev $log_2(10)$ as an SD59x18 number.
int256 constant uLOG2_10 = 3_321928094887362347;
SD59x18 constant LOG2_10 = SD59x18.wrap(uLOG2_10);
/// @dev $log_2(e)$ as an SD59x18 number.
int256 constant uLOG2_E = 1_442695040888963407;
SD59x18 constant LOG2_E = SD59x18.wrap(uLOG2_E);
/// @dev The maximum value an SD59x18 number can have.
int256 constant uMAX_SD59x18 = 57896044618658097711785492504343953926634992332820282019728_792003956564819967;
SD59x18 constant MAX_SD59x18 = SD59x18.wrap(uMAX_SD59x18);
/// @dev The maximum whole value an SD59x18 number can have.
int256 constant uMAX_WHOLE_SD59x18 = 57896044618658097711785492504343953926634992332820282019728_000000000000000000;
SD59x18 constant MAX_WHOLE_SD59x18 = SD59x18.wrap(uMAX_WHOLE_SD59x18);
/// @dev The minimum value an SD59x18 number can have.
int256 constant uMIN_SD59x18 = -57896044618658097711785492504343953926634992332820282019728_792003956564819968;
SD59x18 constant MIN_SD59x18 = SD59x18.wrap(uMIN_SD59x18);
/// @dev The minimum whole value an SD59x18 number can have.
int256 constant uMIN_WHOLE_SD59x18 = -57896044618658097711785492504343953926634992332820282019728_000000000000000000;
SD59x18 constant MIN_WHOLE_SD59x18 = SD59x18.wrap(uMIN_WHOLE_SD59x18);
/// @dev PI as an SD59x18 number.
SD59x18 constant PI = SD59x18.wrap(3_141592653589793238);
/// @dev The unit number, which gives the decimal precision of SD59x18.
int256 constant uUNIT = 1e18;
SD59x18 constant UNIT = SD59x18.wrap(1e18);
/// @dev The unit number squared.
int256 constant uUNIT_SQUARED = 1e36;
SD59x18 constant UNIT_SQUARED = SD59x18.wrap(uUNIT_SQUARED);
/// @dev Zero as an SD59x18 number.
SD59x18 constant ZERO = SD59x18.wrap(0);// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "./Casting.sol" as Casting;
/// @notice The signed 1.18-decimal fixed-point number representation, which can have up to 1 digit and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity
/// type int64. This is useful when end users want to use int64 to save gas, e.g. with tight variable packing in contract
/// storage.
type SD1x18 is int64;
/*//////////////////////////////////////////////////////////////////////////
CASTING
//////////////////////////////////////////////////////////////////////////*/
using {
Casting.intoSD59x18,
Casting.intoUD2x18,
Casting.intoUD60x18,
Casting.intoUint256,
Casting.intoUint128,
Casting.intoUint40,
Casting.unwrap
} for SD1x18 global;// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { SD1x18 } from "./ValueType.sol";
/// @dev Euler's number as an SD1x18 number.
SD1x18 constant E = SD1x18.wrap(2_718281828459045235);
/// @dev The maximum value an SD1x18 number can have.
int64 constant uMAX_SD1x18 = 9_223372036854775807;
SD1x18 constant MAX_SD1x18 = SD1x18.wrap(uMAX_SD1x18);
/// @dev The maximum value an SD1x18 number can have.
int64 constant uMIN_SD1x18 = -9_223372036854775808;
SD1x18 constant MIN_SD1x18 = SD1x18.wrap(uMIN_SD1x18);
/// @dev PI as an SD1x18 number.
SD1x18 constant PI = SD1x18.wrap(3_141592653589793238);
/// @dev The unit number, which gives the decimal precision of SD1x18.
SD1x18 constant UNIT = SD1x18.wrap(1e18);
int64 constant uUNIT = 1e18;// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import { uMAX_SD1x18 } from "../sd1x18/Constants.sol";
import { SD1x18 } from "../sd1x18/ValueType.sol";
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";
import { UD2x18 } from "./ValueType.sol";
/// @notice Casts a UD2x18 number into SD1x18.
/// - x must be less than or equal to `uMAX_SD1x18`.
function intoSD1x18(UD2x18 x) pure returns (SD1x18 result) {
uint64 xUint = UD2x18.unwrap(x);
if (xUint > uint64(uMAX_SD1x18)) {
revert Errors.PRBMath_UD2x18_IntoSD1x18_Overflow(x);
}
result = SD1x18.wrap(int64(xUint));
}
/// @notice Casts a UD2x18 number into SD59x18.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of SD59x18.
function intoSD59x18(UD2x18 x) pure returns (SD59x18 result) {
result = SD59x18.wrap(int256(uint256(UD2x18.unwrap(x))));
}
/// @notice Casts a UD2x18 number into UD60x18.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of UD60x18.
function intoUD60x18(UD2x18 x) pure returns (UD60x18 result) {
result = UD60x18.wrap(UD2x18.unwrap(x));
}
/// @notice Casts a UD2x18 number into uint128.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of uint128.
function intoUint128(UD2x18 x) pure returns (uint128 result) {
result = uint128(UD2x18.unwrap(x));
}
/// @notice Casts a UD2x18 number into uint256.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of uint256.
function intoUint256(UD2x18 x) pure returns (uint256 result) {
result = uint256(UD2x18.unwrap(x));
}
/// @notice Casts a UD2x18 number into uint40.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(UD2x18 x) pure returns (uint40 result) {
uint64 xUint = UD2x18.unwrap(x);
if (xUint > uint64(Common.MAX_UINT40)) {
revert Errors.PRBMath_UD2x18_IntoUint40_Overflow(x);
}
result = uint40(xUint);
}
/// @notice Alias for {wrap}.
function ud2x18(uint64 x) pure returns (UD2x18 result) {
result = UD2x18.wrap(x);
}
/// @notice Unwrap a UD2x18 number into uint64.
function unwrap(UD2x18 x) pure returns (uint64 result) {
result = UD2x18.unwrap(x);
}
/// @notice Wraps a uint64 number into UD2x18.
function wrap(uint64 x) pure returns (UD2x18 result) {
result = UD2x18.wrap(x);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import {
uEXP_MAX_INPUT,
uEXP2_MAX_INPUT,
uEXP_MIN_THRESHOLD,
uEXP2_MIN_THRESHOLD,
uHALF_UNIT,
uLOG2_10,
uLOG2_E,
uMAX_SD59x18,
uMAX_WHOLE_SD59x18,
uMIN_SD59x18,
uMIN_WHOLE_SD59x18,
UNIT,
uUNIT,
uUNIT_SQUARED,
ZERO
} from "./Constants.sol";
import { wrap } from "./Helpers.sol";
import { SD59x18 } from "./ValueType.sol";
/// @notice Calculates the absolute value of x.
///
/// @dev Requirements:
/// - x must be greater than `MIN_SD59x18`.
///
/// @param x The SD59x18 number for which to calculate the absolute value.
/// @param result The absolute value of x as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function abs(SD59x18 x) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
if (xInt == uMIN_SD59x18) {
revert Errors.PRBMath_SD59x18_Abs_MinSD59x18();
}
result = xInt < 0 ? wrap(-xInt) : x;
}
/// @notice Calculates the arithmetic average of x and y.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// @param x The first operand as an SD59x18 number.
/// @param y The second operand as an SD59x18 number.
/// @return result The arithmetic average as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function avg(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
int256 yInt = y.unwrap();
unchecked {
// This operation is equivalent to `x / 2 + y / 2`, and it can never overflow.
int256 sum = (xInt >> 1) + (yInt >> 1);
if (sum < 0) {
// If at least one of x and y is odd, add 1 to the result, because shifting negative numbers to the right
// rounds toward negative infinity. The right part is equivalent to `sum + (x % 2 == 1 || y % 2 == 1)`.
assembly ("memory-safe") {
result := add(sum, and(or(xInt, yInt), 1))
}
} else {
// Add 1 if both x and y are odd to account for the double 0.5 remainder truncated after shifting.
result = wrap(sum + (xInt & yInt & 1));
}
}
}
/// @notice Yields the smallest whole number greater than or equal to x.
///
/// @dev Optimized for fractional value inputs, because every whole value has (1e18 - 1) fractional counterparts.
/// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x must be less than or equal to `MAX_WHOLE_SD59x18`.
///
/// @param x The SD59x18 number to ceil.
/// @param result The smallest whole number greater than or equal to x, as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function ceil(SD59x18 x) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
if (xInt > uMAX_WHOLE_SD59x18) {
revert Errors.PRBMath_SD59x18_Ceil_Overflow(x);
}
int256 remainder = xInt % uUNIT;
if (remainder == 0) {
result = x;
} else {
unchecked {
// Solidity uses C fmod style, which returns a modulus with the same sign as x.
int256 resultInt = xInt - remainder;
if (xInt > 0) {
resultInt += uUNIT;
}
result = wrap(resultInt);
}
}
}
/// @notice Divides two SD59x18 numbers, returning a new SD59x18 number.
///
/// @dev This is an extension of {Common.mulDiv} for signed numbers, which works by computing the signs and the absolute
/// values separately.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
/// - The result is rounded toward zero.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
/// - None of the inputs can be `MIN_SD59x18`.
/// - The denominator must not be zero.
/// - The result must fit in SD59x18.
///
/// @param x The numerator as an SD59x18 number.
/// @param y The denominator as an SD59x18 number.
/// @param result The quotient as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function div(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
int256 yInt = y.unwrap();
if (xInt == uMIN_SD59x18 || yInt == uMIN_SD59x18) {
revert Errors.PRBMath_SD59x18_Div_InputTooSmall();
}
// Get hold of the absolute values of x and y.
uint256 xAbs;
uint256 yAbs;
unchecked {
xAbs = xInt < 0 ? uint256(-xInt) : uint256(xInt);
yAbs = yInt < 0 ? uint256(-yInt) : uint256(yInt);
}
// Compute the absolute value (x*UNIT÷y). The resulting value must fit in SD59x18.
uint256 resultAbs = Common.mulDiv(xAbs, uint256(uUNIT), yAbs);
if (resultAbs > uint256(uMAX_SD59x18)) {
revert Errors.PRBMath_SD59x18_Div_Overflow(x, y);
}
// Check if x and y have the same sign using two's complement representation. The left-most bit represents the sign (1 for
// negative, 0 for positive or zero).
bool sameSign = (xInt ^ yInt) > -1;
// If the inputs have the same sign, the result should be positive. Otherwise, it should be negative.
unchecked {
result = wrap(sameSign ? int256(resultAbs) : -int256(resultAbs));
}
}
/// @notice Calculates the natural exponent of x using the following formula:
///
/// $$
/// e^x = 2^{x * log_2{e}}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {exp2}.
///
/// Requirements:
/// - Refer to the requirements in {exp2}.
/// - x must be less than 133_084258667509499441.
///
/// @param x The exponent as an SD59x18 number.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp(SD59x18 x) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
// Any input less than the threshold returns zero.
// This check also prevents an overflow for very small numbers.
if (xInt < uEXP_MIN_THRESHOLD) {
return ZERO;
}
// This check prevents values greater than 192e18 from being passed to {exp2}.
if (xInt > uEXP_MAX_INPUT) {
revert Errors.PRBMath_SD59x18_Exp_InputTooBig(x);
}
unchecked {
// Inline the fixed-point multiplication to save gas.
int256 doubleUnitProduct = xInt * uLOG2_E;
result = exp2(wrap(doubleUnitProduct / uUNIT));
}
}
/// @notice Calculates the binary exponent of x using the binary fraction method using the following formula:
///
/// $$
/// 2^{-x} = \frac{1}{2^x}
/// $$
///
/// @dev See https://ethereum.stackexchange.com/q/79903/24693.
///
/// Notes:
/// - If x is less than -59_794705707972522261, the result is zero.
///
/// Requirements:
/// - x must be less than 192e18.
/// - The result must fit in SD59x18.
///
/// @param x The exponent as an SD59x18 number.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp2(SD59x18 x) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
if (xInt < 0) {
// The inverse of any number less than the threshold is truncated to zero.
if (xInt < uEXP2_MIN_THRESHOLD) {
return ZERO;
}
unchecked {
// Inline the fixed-point inversion to save gas.
result = wrap(uUNIT_SQUARED / exp2(wrap(-xInt)).unwrap());
}
} else {
// Numbers greater than or equal to 192e18 don't fit in the 192.64-bit format.
if (xInt > uEXP2_MAX_INPUT) {
revert Errors.PRBMath_SD59x18_Exp2_InputTooBig(x);
}
unchecked {
// Convert x to the 192.64-bit fixed-point format.
uint256 x_192x64 = uint256((xInt << 64) / uUNIT);
// It is safe to cast the result to int256 due to the checks above.
result = wrap(int256(Common.exp2(x_192x64)));
}
}
}
/// @notice Yields the greatest whole number less than or equal to x.
///
/// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional
/// counterparts. See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x must be greater than or equal to `MIN_WHOLE_SD59x18`.
///
/// @param x The SD59x18 number to floor.
/// @param result The greatest whole number less than or equal to x, as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function floor(SD59x18 x) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
if (xInt < uMIN_WHOLE_SD59x18) {
revert Errors.PRBMath_SD59x18_Floor_Underflow(x);
}
int256 remainder = xInt % uUNIT;
if (remainder == 0) {
result = x;
} else {
unchecked {
// Solidity uses C fmod style, which returns a modulus with the same sign as x.
int256 resultInt = xInt - remainder;
if (xInt < 0) {
resultInt -= uUNIT;
}
result = wrap(resultInt);
}
}
}
/// @notice Yields the excess beyond the floor of x for positive numbers and the part of the number to the right.
/// of the radix point for negative numbers.
/// @dev Based on the odd function definition. https://en.wikipedia.org/wiki/Fractional_part
/// @param x The SD59x18 number to get the fractional part of.
/// @param result The fractional part of x as an SD59x18 number.
function frac(SD59x18 x) pure returns (SD59x18 result) {
result = wrap(x.unwrap() % uUNIT);
}
/// @notice Calculates the geometric mean of x and y, i.e. $\sqrt{x * y}$.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x * y must fit in SD59x18.
/// - x * y must not be negative, since complex numbers are not supported.
///
/// @param x The first operand as an SD59x18 number.
/// @param y The second operand as an SD59x18 number.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function gm(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
int256 yInt = y.unwrap();
if (xInt == 0 || yInt == 0) {
return ZERO;
}
unchecked {
// Equivalent to `xy / x != y`. Checking for overflow this way is faster than letting Solidity do it.
int256 xyInt = xInt * yInt;
if (xyInt / xInt != yInt) {
revert Errors.PRBMath_SD59x18_Gm_Overflow(x, y);
}
// The product must not be negative, since complex numbers are not supported.
if (xyInt < 0) {
revert Errors.PRBMath_SD59x18_Gm_NegativeProduct(x, y);
}
// We don't need to multiply the result by `UNIT` here because the x*y product picked up a factor of `UNIT`
// during multiplication. See the comments in {Common.sqrt}.
uint256 resultUint = Common.sqrt(uint256(xyInt));
result = wrap(int256(resultUint));
}
}
/// @notice Calculates the inverse of x.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x must not be zero.
///
/// @param x The SD59x18 number for which to calculate the inverse.
/// @return result The inverse as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function inv(SD59x18 x) pure returns (SD59x18 result) {
result = wrap(uUNIT_SQUARED / x.unwrap());
}
/// @notice Calculates the natural logarithm of x using the following formula:
///
/// $$
/// ln{x} = log_2{x} / log_2{e}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
/// - The precision isn't sufficiently fine-grained to return exactly `UNIT` when the input is `E`.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The SD59x18 number for which to calculate the natural logarithm.
/// @return result The natural logarithm as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function ln(SD59x18 x) pure returns (SD59x18 result) {
// Inline the fixed-point multiplication to save gas. This is overflow-safe because the maximum value that
// {log2} can return is ~195_205294292027477728.
result = wrap(log2(x).unwrap() * uUNIT / uLOG2_E);
}
/// @notice Calculates the common logarithm of x using the following formula:
///
/// $$
/// log_{10}{x} = log_2{x} / log_2{10}
/// $$
///
/// However, if x is an exact power of ten, a hard coded value is returned.
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The SD59x18 number for which to calculate the common logarithm.
/// @return result The common logarithm as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function log10(SD59x18 x) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
if (xInt < 0) {
revert Errors.PRBMath_SD59x18_Log_InputTooSmall(x);
}
// Note that the `mul` in this block is the standard multiplication operation, not {SD59x18.mul}.
// prettier-ignore
assembly ("memory-safe") {
switch x
case 1 { result := mul(uUNIT, sub(0, 18)) }
case 10 { result := mul(uUNIT, sub(1, 18)) }
case 100 { result := mul(uUNIT, sub(2, 18)) }
case 1000 { result := mul(uUNIT, sub(3, 18)) }
case 10000 { result := mul(uUNIT, sub(4, 18)) }
case 100000 { result := mul(uUNIT, sub(5, 18)) }
case 1000000 { result := mul(uUNIT, sub(6, 18)) }
case 10000000 { result := mul(uUNIT, sub(7, 18)) }
case 100000000 { result := mul(uUNIT, sub(8, 18)) }
case 1000000000 { result := mul(uUNIT, sub(9, 18)) }
case 10000000000 { result := mul(uUNIT, sub(10, 18)) }
case 100000000000 { result := mul(uUNIT, sub(11, 18)) }
case 1000000000000 { result := mul(uUNIT, sub(12, 18)) }
case 10000000000000 { result := mul(uUNIT, sub(13, 18)) }
case 100000000000000 { result := mul(uUNIT, sub(14, 18)) }
case 1000000000000000 { result := mul(uUNIT, sub(15, 18)) }
case 10000000000000000 { result := mul(uUNIT, sub(16, 18)) }
case 100000000000000000 { result := mul(uUNIT, sub(17, 18)) }
case 1000000000000000000 { result := 0 }
case 10000000000000000000 { result := uUNIT }
case 100000000000000000000 { result := mul(uUNIT, 2) }
case 1000000000000000000000 { result := mul(uUNIT, 3) }
case 10000000000000000000000 { result := mul(uUNIT, 4) }
case 100000000000000000000000 { result := mul(uUNIT, 5) }
case 1000000000000000000000000 { result := mul(uUNIT, 6) }
case 10000000000000000000000000 { result := mul(uUNIT, 7) }
case 100000000000000000000000000 { result := mul(uUNIT, 8) }
case 1000000000000000000000000000 { result := mul(uUNIT, 9) }
case 10000000000000000000000000000 { result := mul(uUNIT, 10) }
case 100000000000000000000000000000 { result := mul(uUNIT, 11) }
case 1000000000000000000000000000000 { result := mul(uUNIT, 12) }
case 10000000000000000000000000000000 { result := mul(uUNIT, 13) }
case 100000000000000000000000000000000 { result := mul(uUNIT, 14) }
case 1000000000000000000000000000000000 { result := mul(uUNIT, 15) }
case 10000000000000000000000000000000000 { result := mul(uUNIT, 16) }
case 100000000000000000000000000000000000 { result := mul(uUNIT, 17) }
case 1000000000000000000000000000000000000 { result := mul(uUNIT, 18) }
case 10000000000000000000000000000000000000 { result := mul(uUNIT, 19) }
case 100000000000000000000000000000000000000 { result := mul(uUNIT, 20) }
case 1000000000000000000000000000000000000000 { result := mul(uUNIT, 21) }
case 10000000000000000000000000000000000000000 { result := mul(uUNIT, 22) }
case 100000000000000000000000000000000000000000 { result := mul(uUNIT, 23) }
case 1000000000000000000000000000000000000000000 { result := mul(uUNIT, 24) }
case 10000000000000000000000000000000000000000000 { result := mul(uUNIT, 25) }
case 100000000000000000000000000000000000000000000 { result := mul(uUNIT, 26) }
case 1000000000000000000000000000000000000000000000 { result := mul(uUNIT, 27) }
case 10000000000000000000000000000000000000000000000 { result := mul(uUNIT, 28) }
case 100000000000000000000000000000000000000000000000 { result := mul(uUNIT, 29) }
case 1000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 30) }
case 10000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 31) }
case 100000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 32) }
case 1000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 33) }
case 10000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 34) }
case 100000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 35) }
case 1000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 36) }
case 10000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 37) }
case 100000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 38) }
case 1000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 39) }
case 10000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 40) }
case 100000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 41) }
case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 42) }
case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 43) }
case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 44) }
case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 45) }
case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 46) }
case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 47) }
case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 48) }
case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 49) }
case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 50) }
case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 51) }
case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 52) }
case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 53) }
case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 54) }
case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 55) }
case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 56) }
case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 57) }
case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 58) }
default { result := uMAX_SD59x18 }
}
if (result.unwrap() == uMAX_SD59x18) {
unchecked {
// Inline the fixed-point division to save gas.
result = wrap(log2(x).unwrap() * uUNIT / uLOG2_10);
}
}
}
/// @notice Calculates the binary logarithm of x using the iterative approximation algorithm:
///
/// $$
/// log_2{x} = n + log_2{y}, \text{ where } y = x*2^{-n}, \ y \in [1, 2)
/// $$
///
/// For $0 \leq x \lt 1$, the input is inverted:
///
/// $$
/// log_2{x} = -log_2{\frac{1}{x}}
/// $$
///
/// @dev See https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation.
///
/// Notes:
/// - Due to the lossy precision of the iterative approximation, the results are not perfectly accurate to the last decimal.
///
/// Requirements:
/// - x must be greater than zero.
///
/// @param x The SD59x18 number for which to calculate the binary logarithm.
/// @return result The binary logarithm as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function log2(SD59x18 x) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
if (xInt <= 0) {
revert Errors.PRBMath_SD59x18_Log_InputTooSmall(x);
}
unchecked {
int256 sign;
if (xInt >= uUNIT) {
sign = 1;
} else {
sign = -1;
// Inline the fixed-point inversion to save gas.
xInt = uUNIT_SQUARED / xInt;
}
// Calculate the integer part of the logarithm.
uint256 n = Common.msb(uint256(xInt / uUNIT));
// This is the integer part of the logarithm as an SD59x18 number. The operation can't overflow
// because n is at most 255, `UNIT` is 1e18, and the sign is either 1 or -1.
int256 resultInt = int256(n) * uUNIT;
// Calculate $y = x * 2^{-n}$.
int256 y = xInt >> n;
// If y is the unit number, the fractional part is zero.
if (y == uUNIT) {
return wrap(resultInt * sign);
}
// Calculate the fractional part via the iterative approximation.
// The `delta >>= 1` part is equivalent to `delta /= 2`, but shifting bits is more gas efficient.
int256 DOUBLE_UNIT = 2e18;
for (int256 delta = uHALF_UNIT; delta > 0; delta >>= 1) {
y = (y * y) / uUNIT;
// Is y^2 >= 2e18 and so in the range [2e18, 4e18)?
if (y >= DOUBLE_UNIT) {
// Add the 2^{-m} factor to the logarithm.
resultInt = resultInt + delta;
// Halve y, which corresponds to z/2 in the Wikipedia article.
y >>= 1;
}
}
resultInt *= sign;
result = wrap(resultInt);
}
}
/// @notice Multiplies two SD59x18 numbers together, returning a new SD59x18 number.
///
/// @dev Notes:
/// - Refer to the notes in {Common.mulDiv18}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv18}.
/// - None of the inputs can be `MIN_SD59x18`.
/// - The result must fit in SD59x18.
///
/// @param x The multiplicand as an SD59x18 number.
/// @param y The multiplier as an SD59x18 number.
/// @return result The product as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function mul(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
int256 yInt = y.unwrap();
if (xInt == uMIN_SD59x18 || yInt == uMIN_SD59x18) {
revert Errors.PRBMath_SD59x18_Mul_InputTooSmall();
}
// Get hold of the absolute values of x and y.
uint256 xAbs;
uint256 yAbs;
unchecked {
xAbs = xInt < 0 ? uint256(-xInt) : uint256(xInt);
yAbs = yInt < 0 ? uint256(-yInt) : uint256(yInt);
}
// Compute the absolute value (x*y÷UNIT). The resulting value must fit in SD59x18.
uint256 resultAbs = Common.mulDiv18(xAbs, yAbs);
if (resultAbs > uint256(uMAX_SD59x18)) {
revert Errors.PRBMath_SD59x18_Mul_Overflow(x, y);
}
// Check if x and y have the same sign using two's complement representation. The left-most bit represents the sign (1 for
// negative, 0 for positive or zero).
bool sameSign = (xInt ^ yInt) > -1;
// If the inputs have the same sign, the result should be positive. Otherwise, it should be negative.
unchecked {
result = wrap(sameSign ? int256(resultAbs) : -int256(resultAbs));
}
}
/// @notice Raises x to the power of y using the following formula:
///
/// $$
/// x^y = 2^{log_2{x} * y}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {exp2}, {log2}, and {mul}.
/// - Returns `UNIT` for 0^0.
///
/// Requirements:
/// - Refer to the requirements in {exp2}, {log2}, and {mul}.
///
/// @param x The base as an SD59x18 number.
/// @param y Exponent to raise x to, as an SD59x18 number
/// @return result x raised to power y, as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function pow(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
int256 yInt = y.unwrap();
// If both x and y are zero, the result is `UNIT`. If just x is zero, the result is always zero.
if (xInt == 0) {
return yInt == 0 ? UNIT : ZERO;
}
// If x is `UNIT`, the result is always `UNIT`.
else if (xInt == uUNIT) {
return UNIT;
}
// If y is zero, the result is always `UNIT`.
if (yInt == 0) {
return UNIT;
}
// If y is `UNIT`, the result is always x.
else if (yInt == uUNIT) {
return x;
}
// Calculate the result using the formula.
result = exp2(mul(log2(x), y));
}
/// @notice Raises x (an SD59x18 number) to the power y (an unsigned basic integer) using the well-known
/// algorithm "exponentiation by squaring".
///
/// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv18}.
/// - Returns `UNIT` for 0^0.
///
/// Requirements:
/// - Refer to the requirements in {abs} and {Common.mulDiv18}.
/// - The result must fit in SD59x18.
///
/// @param x The base as an SD59x18 number.
/// @param y The exponent as a uint256.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function powu(SD59x18 x, uint256 y) pure returns (SD59x18 result) {
uint256 xAbs = uint256(abs(x).unwrap());
// Calculate the first iteration of the loop in advance.
uint256 resultAbs = y & 1 > 0 ? xAbs : uint256(uUNIT);
// Equivalent to `for(y /= 2; y > 0; y /= 2)`.
uint256 yAux = y;
for (yAux >>= 1; yAux > 0; yAux >>= 1) {
xAbs = Common.mulDiv18(xAbs, xAbs);
// Equivalent to `y % 2 == 1`.
if (yAux & 1 > 0) {
resultAbs = Common.mulDiv18(resultAbs, xAbs);
}
}
// The result must fit in SD59x18.
if (resultAbs > uint256(uMAX_SD59x18)) {
revert Errors.PRBMath_SD59x18_Powu_Overflow(x, y);
}
unchecked {
// Is the base negative and the exponent odd? If yes, the result should be negative.
int256 resultInt = int256(resultAbs);
bool isNegative = x.unwrap() < 0 && y & 1 == 1;
if (isNegative) {
resultInt = -resultInt;
}
result = wrap(resultInt);
}
}
/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - Only the positive root is returned.
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x cannot be negative, since complex numbers are not supported.
/// - x must be less than `MAX_SD59x18 / UNIT`.
///
/// @param x The SD59x18 number for which to calculate the square root.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function sqrt(SD59x18 x) pure returns (SD59x18 result) {
int256 xInt = x.unwrap();
if (xInt < 0) {
revert Errors.PRBMath_SD59x18_Sqrt_NegativeInput(x);
}
if (xInt > uMAX_SD59x18 / uUNIT) {
revert Errors.PRBMath_SD59x18_Sqrt_Overflow(x);
}
unchecked {
// Multiply x by `UNIT` to account for the factor of `UNIT` picked up when multiplying two SD59x18 numbers.
// In this case, the two numbers are both the square root.
uint256 resultUint = Common.sqrt(uint256(xInt * uUNIT));
result = wrap(int256(resultUint));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { wrap } from "./Casting.sol";
import { SD59x18 } from "./ValueType.sol";
/// @notice Implements the checked addition operation (+) in the SD59x18 type.
function add(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
return wrap(x.unwrap() + y.unwrap());
}
/// @notice Implements the AND (&) bitwise operation in the SD59x18 type.
function and(SD59x18 x, int256 bits) pure returns (SD59x18 result) {
return wrap(x.unwrap() & bits);
}
/// @notice Implements the AND (&) bitwise operation in the SD59x18 type.
function and2(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
return wrap(x.unwrap() & y.unwrap());
}
/// @notice Implements the equal (=) operation in the SD59x18 type.
function eq(SD59x18 x, SD59x18 y) pure returns (bool result) {
result = x.unwrap() == y.unwrap();
}
/// @notice Implements the greater than operation (>) in the SD59x18 type.
function gt(SD59x18 x, SD59x18 y) pure returns (bool result) {
result = x.unwrap() > y.unwrap();
}
/// @notice Implements the greater than or equal to operation (>=) in the SD59x18 type.
function gte(SD59x18 x, SD59x18 y) pure returns (bool result) {
result = x.unwrap() >= y.unwrap();
}
/// @notice Implements a zero comparison check function in the SD59x18 type.
function isZero(SD59x18 x) pure returns (bool result) {
result = x.unwrap() == 0;
}
/// @notice Implements the left shift operation (<<) in the SD59x18 type.
function lshift(SD59x18 x, uint256 bits) pure returns (SD59x18 result) {
result = wrap(x.unwrap() << bits);
}
/// @notice Implements the lower than operation (<) in the SD59x18 type.
function lt(SD59x18 x, SD59x18 y) pure returns (bool result) {
result = x.unwrap() < y.unwrap();
}
/// @notice Implements the lower than or equal to operation (<=) in the SD59x18 type.
function lte(SD59x18 x, SD59x18 y) pure returns (bool result) {
result = x.unwrap() <= y.unwrap();
}
/// @notice Implements the unchecked modulo operation (%) in the SD59x18 type.
function mod(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
result = wrap(x.unwrap() % y.unwrap());
}
/// @notice Implements the not equal operation (!=) in the SD59x18 type.
function neq(SD59x18 x, SD59x18 y) pure returns (bool result) {
result = x.unwrap() != y.unwrap();
}
/// @notice Implements the NOT (~) bitwise operation in the SD59x18 type.
function not(SD59x18 x) pure returns (SD59x18 result) {
result = wrap(~x.unwrap());
}
/// @notice Implements the OR (|) bitwise operation in the SD59x18 type.
function or(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
result = wrap(x.unwrap() | y.unwrap());
}
/// @notice Implements the right shift operation (>>) in the SD59x18 type.
function rshift(SD59x18 x, uint256 bits) pure returns (SD59x18 result) {
result = wrap(x.unwrap() >> bits);
}
/// @notice Implements the checked subtraction operation (-) in the SD59x18 type.
function sub(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
result = wrap(x.unwrap() - y.unwrap());
}
/// @notice Implements the checked unary minus operation (-) in the SD59x18 type.
function unary(SD59x18 x) pure returns (SD59x18 result) {
result = wrap(-x.unwrap());
}
/// @notice Implements the unchecked addition operation (+) in the SD59x18 type.
function uncheckedAdd(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
unchecked {
result = wrap(x.unwrap() + y.unwrap());
}
}
/// @notice Implements the unchecked subtraction operation (-) in the SD59x18 type.
function uncheckedSub(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
unchecked {
result = wrap(x.unwrap() - y.unwrap());
}
}
/// @notice Implements the unchecked unary minus operation (-) in the SD59x18 type.
function uncheckedUnary(SD59x18 x) pure returns (SD59x18 result) {
unchecked {
result = wrap(-x.unwrap());
}
}
/// @notice Implements the XOR (^) bitwise operation in the SD59x18 type.
function xor(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
result = wrap(x.unwrap() ^ y.unwrap());
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "./Errors.sol" as CastingErrors;
import { MAX_UINT128, MAX_UINT40 } from "../Common.sol";
import { uMAX_SD1x18, uMIN_SD1x18 } from "../sd1x18/Constants.sol";
import { SD1x18 } from "../sd1x18/ValueType.sol";
import { uMAX_UD2x18 } from "../ud2x18/Constants.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";
import { SD59x18 } from "./ValueType.sol";
/// @notice Casts an SD59x18 number into int256.
/// @dev This is basically a functional alias for {unwrap}.
function intoInt256(SD59x18 x) pure returns (int256 result) {
result = SD59x18.unwrap(x);
}
/// @notice Casts an SD59x18 number into SD1x18.
/// @dev Requirements:
/// - x must be greater than or equal to `uMIN_SD1x18`.
/// - x must be less than or equal to `uMAX_SD1x18`.
function intoSD1x18(SD59x18 x) pure returns (SD1x18 result) {
int256 xInt = SD59x18.unwrap(x);
if (xInt < uMIN_SD1x18) {
revert CastingErrors.PRBMath_SD59x18_IntoSD1x18_Underflow(x);
}
if (xInt > uMAX_SD1x18) {
revert CastingErrors.PRBMath_SD59x18_IntoSD1x18_Overflow(x);
}
result = SD1x18.wrap(int64(xInt));
}
/// @notice Casts an SD59x18 number into UD2x18.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `uMAX_UD2x18`.
function intoUD2x18(SD59x18 x) pure returns (UD2x18 result) {
int256 xInt = SD59x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD59x18_IntoUD2x18_Underflow(x);
}
if (xInt > int256(uint256(uMAX_UD2x18))) {
revert CastingErrors.PRBMath_SD59x18_IntoUD2x18_Overflow(x);
}
result = UD2x18.wrap(uint64(uint256(xInt)));
}
/// @notice Casts an SD59x18 number into UD60x18.
/// @dev Requirements:
/// - x must be positive.
function intoUD60x18(SD59x18 x) pure returns (UD60x18 result) {
int256 xInt = SD59x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD59x18_IntoUD60x18_Underflow(x);
}
result = UD60x18.wrap(uint256(xInt));
}
/// @notice Casts an SD59x18 number into uint256.
/// @dev Requirements:
/// - x must be positive.
function intoUint256(SD59x18 x) pure returns (uint256 result) {
int256 xInt = SD59x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD59x18_IntoUint256_Underflow(x);
}
result = uint256(xInt);
}
/// @notice Casts an SD59x18 number into uint128.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `uMAX_UINT128`.
function intoUint128(SD59x18 x) pure returns (uint128 result) {
int256 xInt = SD59x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD59x18_IntoUint128_Underflow(x);
}
if (xInt > int256(uint256(MAX_UINT128))) {
revert CastingErrors.PRBMath_SD59x18_IntoUint128_Overflow(x);
}
result = uint128(uint256(xInt));
}
/// @notice Casts an SD59x18 number into uint40.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(SD59x18 x) pure returns (uint40 result) {
int256 xInt = SD59x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD59x18_IntoUint40_Underflow(x);
}
if (xInt > int256(uint256(MAX_UINT40))) {
revert CastingErrors.PRBMath_SD59x18_IntoUint40_Overflow(x);
}
result = uint40(uint256(xInt));
}
/// @notice Alias for {wrap}.
function sd(int256 x) pure returns (SD59x18 result) {
result = SD59x18.wrap(x);
}
/// @notice Alias for {wrap}.
function sd59x18(int256 x) pure returns (SD59x18 result) {
result = SD59x18.wrap(x);
}
/// @notice Unwraps an SD59x18 number into int256.
function unwrap(SD59x18 x) pure returns (int256 result) {
result = SD59x18.unwrap(x);
}
/// @notice Wraps an int256 number into SD59x18.
function wrap(int256 x) pure returns (SD59x18 result) {
result = SD59x18.wrap(x);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "../Common.sol" as Common;
import "./Errors.sol" as CastingErrors;
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";
import { SD1x18 } from "./ValueType.sol";
/// @notice Casts an SD1x18 number into SD59x18.
/// @dev There is no overflow check because the domain of SD1x18 is a subset of SD59x18.
function intoSD59x18(SD1x18 x) pure returns (SD59x18 result) {
result = SD59x18.wrap(int256(SD1x18.unwrap(x)));
}
/// @notice Casts an SD1x18 number into UD2x18.
/// - x must be positive.
function intoUD2x18(SD1x18 x) pure returns (UD2x18 result) {
int64 xInt = SD1x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD1x18_ToUD2x18_Underflow(x);
}
result = UD2x18.wrap(uint64(xInt));
}
/// @notice Casts an SD1x18 number into UD60x18.
/// @dev Requirements:
/// - x must be positive.
function intoUD60x18(SD1x18 x) pure returns (UD60x18 result) {
int64 xInt = SD1x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD1x18_ToUD60x18_Underflow(x);
}
result = UD60x18.wrap(uint64(xInt));
}
/// @notice Casts an SD1x18 number into uint256.
/// @dev Requirements:
/// - x must be positive.
function intoUint256(SD1x18 x) pure returns (uint256 result) {
int64 xInt = SD1x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD1x18_ToUint256_Underflow(x);
}
result = uint256(uint64(xInt));
}
/// @notice Casts an SD1x18 number into uint128.
/// @dev Requirements:
/// - x must be positive.
function intoUint128(SD1x18 x) pure returns (uint128 result) {
int64 xInt = SD1x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD1x18_ToUint128_Underflow(x);
}
result = uint128(uint64(xInt));
}
/// @notice Casts an SD1x18 number into uint40.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(SD1x18 x) pure returns (uint40 result) {
int64 xInt = SD1x18.unwrap(x);
if (xInt < 0) {
revert CastingErrors.PRBMath_SD1x18_ToUint40_Underflow(x);
}
if (xInt > int64(uint64(Common.MAX_UINT40))) {
revert CastingErrors.PRBMath_SD1x18_ToUint40_Overflow(x);
}
result = uint40(uint64(xInt));
}
/// @notice Alias for {wrap}.
function sd1x18(int64 x) pure returns (SD1x18 result) {
result = SD1x18.wrap(x);
}
/// @notice Unwraps an SD1x18 number into int64.
function unwrap(SD1x18 x) pure returns (int64 result) {
result = SD1x18.unwrap(x);
}
/// @notice Wraps an int64 number into SD1x18.
function wrap(int64 x) pure returns (SD1x18 result) {
result = SD1x18.wrap(x);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { UD2x18 } from "./ValueType.sol";
/// @notice Thrown when trying to cast a UD2x18 number that doesn't fit in SD1x18.
error PRBMath_UD2x18_IntoSD1x18_Overflow(UD2x18 x);
/// @notice Thrown when trying to cast a UD2x18 number that doesn't fit in uint40.
error PRBMath_UD2x18_IntoUint40_Overflow(UD2x18 x);// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { SD59x18 } from "./ValueType.sol";
/// @notice Thrown when taking the absolute value of `MIN_SD59x18`.
error PRBMath_SD59x18_Abs_MinSD59x18();
/// @notice Thrown when ceiling a number overflows SD59x18.
error PRBMath_SD59x18_Ceil_Overflow(SD59x18 x);
/// @notice Thrown when converting a basic integer to the fixed-point format overflows SD59x18.
error PRBMath_SD59x18_Convert_Overflow(int256 x);
/// @notice Thrown when converting a basic integer to the fixed-point format underflows SD59x18.
error PRBMath_SD59x18_Convert_Underflow(int256 x);
/// @notice Thrown when dividing two numbers and one of them is `MIN_SD59x18`.
error PRBMath_SD59x18_Div_InputTooSmall();
/// @notice Thrown when dividing two numbers and one of the intermediary unsigned results overflows SD59x18.
error PRBMath_SD59x18_Div_Overflow(SD59x18 x, SD59x18 y);
/// @notice Thrown when taking the natural exponent of a base greater than 133_084258667509499441.
error PRBMath_SD59x18_Exp_InputTooBig(SD59x18 x);
/// @notice Thrown when taking the binary exponent of a base greater than 192e18.
error PRBMath_SD59x18_Exp2_InputTooBig(SD59x18 x);
/// @notice Thrown when flooring a number underflows SD59x18.
error PRBMath_SD59x18_Floor_Underflow(SD59x18 x);
/// @notice Thrown when taking the geometric mean of two numbers and their product is negative.
error PRBMath_SD59x18_Gm_NegativeProduct(SD59x18 x, SD59x18 y);
/// @notice Thrown when taking the geometric mean of two numbers and multiplying them overflows SD59x18.
error PRBMath_SD59x18_Gm_Overflow(SD59x18 x, SD59x18 y);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18.
error PRBMath_SD59x18_IntoSD1x18_Overflow(SD59x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18.
error PRBMath_SD59x18_IntoSD1x18_Underflow(SD59x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18.
error PRBMath_SD59x18_IntoUD2x18_Overflow(SD59x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18.
error PRBMath_SD59x18_IntoUD2x18_Underflow(SD59x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD60x18.
error PRBMath_SD59x18_IntoUD60x18_Underflow(SD59x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128.
error PRBMath_SD59x18_IntoUint128_Overflow(SD59x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128.
error PRBMath_SD59x18_IntoUint128_Underflow(SD59x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint256.
error PRBMath_SD59x18_IntoUint256_Underflow(SD59x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint40.
error PRBMath_SD59x18_IntoUint40_Overflow(SD59x18 x);
/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint40.
error PRBMath_SD59x18_IntoUint40_Underflow(SD59x18 x);
/// @notice Thrown when taking the logarithm of a number less than or equal to zero.
error PRBMath_SD59x18_Log_InputTooSmall(SD59x18 x);
/// @notice Thrown when multiplying two numbers and one of the inputs is `MIN_SD59x18`.
error PRBMath_SD59x18_Mul_InputTooSmall();
/// @notice Thrown when multiplying two numbers and the intermediary absolute result overflows SD59x18.
error PRBMath_SD59x18_Mul_Overflow(SD59x18 x, SD59x18 y);
/// @notice Thrown when raising a number to a power and the intermediary absolute result overflows SD59x18.
error PRBMath_SD59x18_Powu_Overflow(SD59x18 x, uint256 y);
/// @notice Thrown when taking the square root of a negative number.
error PRBMath_SD59x18_Sqrt_NegativeInput(SD59x18 x);
/// @notice Thrown when the calculating the square root overflows SD59x18.
error PRBMath_SD59x18_Sqrt_Overflow(SD59x18 x);// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import { SD1x18 } from "./ValueType.sol";
/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in UD2x18.
error PRBMath_SD1x18_ToUD2x18_Underflow(SD1x18 x);
/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in UD60x18.
error PRBMath_SD1x18_ToUD60x18_Underflow(SD1x18 x);
/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint128.
error PRBMath_SD1x18_ToUint128_Underflow(SD1x18 x);
/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint256.
error PRBMath_SD1x18_ToUint256_Underflow(SD1x18 x);
/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint40.
error PRBMath_SD1x18_ToUint40_Overflow(SD1x18 x);
/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint40.
error PRBMath_SD1x18_ToUint40_Underflow(SD1x18 x);{
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": [
"forge-std/=lib/forge-std/src/",
"@prb/math/=lib/prb-math/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/"
]
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CAMPAIGN_PAUSED","type":"error"},{"inputs":[],"name":"CHECK_QAS_OR_KPIS","type":"error"},{"inputs":[],"name":"DEPRECATED_MODEL","type":"error"},{"inputs":[{"internalType":"string","name":"kpi","type":"string"}],"name":"DUPLICATE_KPI","type":"error"},{"inputs":[{"internalType":"string","name":"qa","type":"string"}],"name":"DUPLICATE_QA","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":"INVALID_CONTENT_LINK","type":"error"},{"inputs":[],"name":"NOT_ELIGIBLE_FOR_WITHDRAW","type":"error"},{"inputs":[],"name":"NO_CONTENT_AUTHOR","type":"error"},{"inputs":[],"name":"ONLY_CLAIMABLE_STATUS","type":"error"},{"inputs":[],"name":"ONLY_CONTENTS","type":"error"},{"inputs":[],"name":"ONLY_FACTORY","type":"error"},{"inputs":[],"name":"ONLY_FINALIZER_ORACLE","type":"error"},{"inputs":[],"name":"ONLY_FINISHED_CAMPAIGN_STATUS","type":"error"},{"inputs":[{"internalType":"enum Status","name":"status","type":"uint8"}],"name":"ONLY_IN_PROGRESS_STATUS","type":"error"},{"inputs":[],"name":"ONLY_OWNER","type":"error"},{"inputs":[],"name":"ONLY_PROTOCOL_ADMIN","type":"error"},{"inputs":[{"internalType":"enum Status","name":"status","type":"uint8"}],"name":"ONLY_UPCOMING_OR_IN_PROGRESS_STATUS","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"addedAmount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"lastBudget","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"newBudget","type":"uint256"},{"indexed":false,"internalType":"address","name":"donator","type":"address"},{"indexed":false,"internalType":"address","name":"campaign","type":"address"}],"name":"BudgetIncreased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"finalizer","type":"address"},{"indexed":true,"internalType":"enum Finalizer","name":"finalizerType","type":"uint8"},{"indexed":true,"internalType":"uint256","name":"refundedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBudget","type":"uint256"},{"indexed":false,"internalType":"address","name":"campaign","type":"address"}],"name":"CampaignFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"finishedAt","type":"uint256"},{"indexed":false,"internalType":"address","name":"campaign","type":"address"}],"name":"CampaignFinished","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bool","name":"paused","type":"bool"},{"indexed":false,"internalType":"address","name":"campaign","type":"address"}],"name":"CampaignPauseToggled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"contentCreator","type":"address"},{"indexed":true,"internalType":"uint256","name":"rewardedAmount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"refundAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalPaidAmount","type":"uint256"},{"indexed":false,"internalType":"string","name":"contentLink","type":"string"},{"indexed":false,"internalType":"address","name":"campaign","type":"address"}],"name":"Claimed","type":"event"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"applicationFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"contentLink","type":"string"}],"name":"applyContent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"budgetInfo","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint128","name":"maxPerPost","type":"uint128"},{"internalType":"uint128","name":"reservedAmount","type":"uint128"}],"internalType":"struct BudgetInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"contentLink","type":"string"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"contentEffectiveKPI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contents","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentStatus","outputs":[{"internalType":"enum Status","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"details","outputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"ipfsCID","type":"string"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"}],"internalType":"struct BasicInfo","name":"basicInfo_","type":"tuple"},{"components":[{"internalType":"uint64","name":"initTime","type":"uint64"},{"internalType":"address","name":"clonedCampaign","type":"address"},{"internalType":"address","name":"implementation","type":"address"}],"internalType":"struct CampaignInitData","name":"initData_","type":"tuple"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint128","name":"maxPerPost","type":"uint128"},{"internalType":"uint128","name":"reservedAmount","type":"uint128"}],"internalType":"struct BudgetInfo","name":"budgetInfo_","type":"tuple"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"decimals","type":"uint256"}],"internalType":"struct Token","name":"token_","type":"tuple"},{"components":[{"internalType":"string","name":"social","type":"string"},{"components":[{"internalType":"uint8","name":"pct","type":"uint8"},{"internalType":"uint248","name":"min","type":"uint248"},{"internalType":"uint256","name":"ratio","type":"uint256"},{"internalType":"string","name":"method","type":"string"},{"internalType":"string","name":"extra","type":"string"}],"internalType":"struct KPI[]","name":"kpis","type":"tuple[]"}],"internalType":"struct SocialKPIs","name":"socialKPIs_","type":"tuple"},{"components":[{"internalType":"enum QA_METHOD","name":"method","type":"uint8"},{"internalType":"uint88","name":"pct","type":"uint88"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"string","name":"kind","type":"string"}],"internalType":"struct QualificationData[]","name":"qaData_","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"totalPosts","type":"uint256"},{"internalType":"uint256","name":"totalEligibleContents","type":"uint256"},{"internalType":"uint256","name":"totalEffectiveKPIs","type":"uint256"},{"internalType":"uint256","name":"lastPostTime","type":"uint256"},{"internalType":"uint256","name":"lastUpdatedTime","type":"uint256"},{"internalType":"uint256","name":"applicationFee","type":"uint256"}],"internalType":"struct PostInfo","name":"postInfo_","type":"tuple"},{"internalType":"enum Status","name":"status_","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"finalizeFinishedCampaign","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"finalizeInProgressCampaign","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"contentLink","type":"string"}],"name":"getContentCreator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"increaseBudget","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsCID","type":"string"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint128","name":"maxPerPost","type":"uint128"},{"internalType":"uint128","name":"reservedAmount","type":"uint128"}],"internalType":"struct BudgetInfo","name":"budgetInfo_","type":"tuple"},{"components":[{"internalType":"string","name":"social","type":"string"},{"components":[{"internalType":"uint8","name":"pct","type":"uint8"},{"internalType":"uint248","name":"min","type":"uint248"},{"internalType":"uint256","name":"ratio","type":"uint256"},{"internalType":"string","name":"method","type":"string"},{"internalType":"string","name":"extra","type":"string"}],"internalType":"struct KPI[]","name":"kpis","type":"tuple[]"}],"internalType":"struct SocialKPIs","name":"socialKPIs_","type":"tuple"},{"components":[{"internalType":"enum QA_METHOD","name":"method","type":"uint8"},{"internalType":"uint88","name":"pct","type":"uint88"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"string","name":"kind","type":"string"}],"internalType":"struct QualificationData[]","name":"qaData_","type":"tuple[]"}],"name":"init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nameHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"qaData","outputs":[{"components":[{"internalType":"enum QA_METHOD","name":"method","type":"uint8"},{"internalType":"uint88","name":"pct","type":"uint88"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"string","name":"kind","type":"string"}],"internalType":"struct QualificationData[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"refundAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"registeredActionKind","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"enum QA_METHOD","name":"method","type":"uint8"},{"internalType":"uint88","name":"pct","type":"uint88"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"string","name":"kind","type":"string"}],"internalType":"struct QualificationData[]","name":"qas","type":"tuple[]"},{"components":[{"internalType":"uint8","name":"pct","type":"uint8"},{"internalType":"uint248","name":"min","type":"uint248"},{"internalType":"uint256","name":"ratio","type":"uint256"},{"internalType":"string","name":"method","type":"string"},{"internalType":"string","name":"extra","type":"string"}],"internalType":"struct KPI[]","name":"kpis","type":"tuple[]"}],"name":"rewardEstimation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"socialKPIs","outputs":[{"components":[{"internalType":"string","name":"social","type":"string"},{"components":[{"internalType":"uint8","name":"pct","type":"uint8"},{"internalType":"uint248","name":"min","type":"uint248"},{"internalType":"uint256","name":"ratio","type":"uint256"},{"internalType":"string","name":"method","type":"string"},{"internalType":"string","name":"extra","type":"string"}],"internalType":"struct KPI[]","name":"kpis","type":"tuple[]"}],"internalType":"struct SocialKPIs","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"togglePauseCampaign","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"contentLink","type":"string"},{"internalType":"uint256","name":"additionalContentEffectiveKPI","type":"uint256"},{"internalType":"bool","name":"additional","type":"bool"}],"name":"updateKPIs","outputs":[{"internalType":"uint256","name":"totalEffectiveKPIs","type":"uint256"},{"internalType":"uint256","name":"contentEffectiveKPI_","type":"uint256"},{"internalType":"uint256","name":"totalEligibleContents","type":"uint256"},{"internalType":"int8","name":"eligibilityPolarity","type":"int8"}],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60a0604052348015600e575f5ffd5b504660fc0360325773fc00000000000000000000000000000000000001608052606f565b466109da0360565773a35bd0843e49f4ecf7fb716fdaddc86f4482db8e608052606f565b73ece1b93b2da75c6ee3b78d158416df18ae9fee156080525b6080516152a96100875f395f611b6f01526152a95ff3fe608060405234801561000f575f5ffd5b50600436106101bb575f3560e01c80638c7a8eb3116100f3578063c45a015511610093578063ef8a92351161006e578063ef8a923514610412578063f172a4ce14610427578063f3fe12c91461042f578063f851a44014610442575f5ffd5b8063c45a0155146103e4578063cdcb3942146103ec578063e8009948146103ff575f5ffd5b8063955c9867116100ce578063955c986714610356578063a7c64c701461035e578063aa588b4b14610393578063b7701e44146103d1575f5ffd5b80638c7a8eb3146103265780638da5cb5b1461033b578063951f823914610343575f5ffd5b80633197cbb61161015e578063565974d311610139578063565974d3146102e757806373e4fc0b1461030357806378e979251461030b5780638980f11f14610313575f5ffd5b80633197cbb6146102615780633326ee9c1461026957806349c26fcb146102d4575f5ffd5b80630ebf14b5116101995780630ebf14b51461020f5780631f55479f1461021957806324034a0e1461022157806326f016371461024c575f5ffd5b806304cbb47f146101bf57806306fdde03146101da5780630cb61f6c146101ef575b5f5ffd5b6101c761044a565b6040519081526020015b60405180910390f35b6101e26107bc565b6040516101d19190613fc3565b6101f7610840565b6040516001600160a01b0390911681526020016101d1565b61021761084b565b005b610217610943565b6101c761022f36600461403f565b8051602081830181018051600b8252928201919093012091525481565b610254610ae1565b6040516101d19190614173565b6101c7610c31565b6102c7604080516060810182525f808252602082018190529181019190915250604080516060810182526005546001600160a01b031681526006546001600160801b038082166020840152600160801b909104169181019190915290565b6040516101d191906141b6565b6102176102e23660046141c4565b610c3c565b6102ef610d96565b6040516101d19897969594939291906142fa565b6102176115b9565b6101c76116f3565b610217610321366004614461565b6116fe565b61032e6118a7565b6040516101d1919061448b565b6101f7611b08565b6102176103513660046144e1565b611b13565b6101f7611d38565b61037161036c36600461452c565b611da0565b604080519485526020850193909352918301525f0b60608201526080016101d1565b6103c16103a136600461403f565b8051602081830181018051600a8252928201919093012091525460ff1681565b60405190151581526020016101d1565b6101c76103df3660046145c6565b611f44565b6101f76125c1565b6101f76103fa3660046144e1565b6125cc565b61021761040d366004614630565b612673565b61041a612bf9565b6040516101d191906146e6565b6101c7612c98565b61021761043d3660046144e1565b612ca8565b6101f7613163565b6009545f9081808060605b84841015610675575f60098581548110610471576104716146f4565b5f91825260209091206002918202015460ff1690811115610494576104946140b8565b036104bd5750604080518082019091526007815266028a096a0a49d160cd1b602082015261053c565b6001600985815481106104d2576104d26146f4565b5f91825260209091206002918202015460ff16908111156104f5576104f56140b8565b0361051d5750604080518082019091526006815265028a096a19d160d51b602082015261053c565b50604080518082019091526006815265028a096ab1d160d51b60208201525b610544613163565b6001600160a01b031663b6001e2460098681548110610565576105656146f4565b905f5260205f2090600202015f01600c9054906101000a90046001600160a01b0316836009888154811061059b5761059b6146f4565b905f5260205f2090600202016001016040516020016105bb9291906147c5565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b81526004016106029291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa15801561061d573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061064191906147fb565b506001600160f81b0316935050821590508061065d5750816001145b15610666575f91505b60019093019291810191610455565b6008546040519095505f94506106909060079060200161484f565b60405160208183030381529060405290505b848410156107b2576106b2613163565b6001600160a01b031663b6001e245f83600760010188815481106106d8576106d86146f4565b905f5260205f2090600402016002016040516020016106f89291906147c5565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b815260040161073f9291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa15801561075a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061077e91906147fb565b506001600160f81b0316935050821590508061079a5750816001145b156107a3575f91505b600190930192918101916106a2565b5090949350505050565b60606107c66125c1565b6001600160a01b031663fe4d55366107dc612c98565b6040518263ffffffff1660e01b81526004016107fa91815260200190565b5f60405180830381865afa158015610814573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261083b919081019061486c565b905090565b5f61083b603c613169565b610853613163565b6001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa15801561088e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108b291906148e0565b6001600160a01b0316336001600160a01b0316146108e3576040516315cead3960e31b815260040160405180910390fd5b5f805460ff62010000808304821615810262ff0000199093169290921792839055604051308152919092049091161515907f771cd98ea23fa6204e979f96eecb381b08abb79f1a85d2a4e7b840086feeb86f9060200160405180910390a2565b5f61094c613163565b6001600160a01b031663b6001e246001604051602001610983907021a7a72324a39d102334b730b634bd32b960791b815260110190565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b81526004016109ca9291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa1580156109e5573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a0991906147fb565b9350505050806001600160a01b0316336001600160a01b031614610a40576040516389dd324f60e01b815260040160405180910390fd5b6003610a4a612bf9565b6005811115610a5b57610a5b6140b8565b14610a79576040516380bb6a1760e01b815260040160405180910390fd5b5f610a8261318d565b6001600160801b0316905080600160065460408051600160801b9092046001600160801b0316825230602083015233917f4bf986268e3c9b6b83f7e5fc1afcd134e539e7ab3188bd6e1a2cbe07ed9837a1910160405180910390a45050565b60606009805480602002602001604051908101604052809291908181526020015f905b82821015610c28578382905f5260205f2090600202016040518060800160405290815f82015f9054906101000a900460ff166002811115610b4757610b476140b8565b6002811115610b5857610b586140b8565b8152815461010081046001600160581b03166020830152600160601b90046001600160a01b03166040820152600182018054606090920191610b999061471f565b80601f0160208091040260200160405190810160405280929190818152602001828054610bc59061471f565b8015610c105780601f10610be757610100808354040283529160200191610c10565b820191905f5260205f20905b815481529060010190602001808311610bf357829003601f168201915b50505050508152505081526020019060010190610b04565b50505050905090565b5f61083b60906133d2565b5f610c45612bf9565b6005811115610c5657610c566140b8565b14158015610c7d57506001610c69612bf9565b6005811115610c7a57610c7a6140b8565b14155b15610cad57610c8a612bf9565b60405163eb37a58560e01b8152600401610ca491906146e6565b60405180910390fd5b6005546040516323b872dd60e01b8152336004820152306024820152604481018390526001600160a01b03909116906323b872dd906064016020604051808303815f875af1158015610d01573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d2591906148fb565b50600680546001600160801b03808216600160801b9283900482168501821683021792839055910416610d58828261492a565b6040805133815230602082015284917f3e5931cb7426e4e39150cbb111cc76ae83a5702ccd97fb254c357bcff6b756c591015b60405180910390a450565b610dce6040518060a0016040528060608152602001606081526020015f6001600160a01b031681526020015f81526020015f81525090565b60408051606080820183525f808352602080840182905283850182905284518084018652828152808201839052808601839052855180850187528481529182019390935293840152909160408051808201909152606080825260208201526060610e616040518060c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f6040518060a00160405280610e756107bc565b815260200160048054610e879061471f565b80601f0160208091040260200160405190810160405280929190818152602001828054610eb39061471f565b8015610efe5780601f10610ed557610100808354040283529160200191610efe565b820191905f5260205f20905b815481529060010190602001808311610ee157829003601f168201915b50505050508152602001610f10611b08565b6001600160a01b03168152602001610f266116f3565b8152602001610f33610c31565b90529750610f3f6125c1565b6001600160a01b0316632c960900610f556107bc565b6040518263ffffffff1660e01b8152600401610f719190613fc3565b606060405180830381865afa158015610f8c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fb0919061493d565b60408051606080820183526005546001600160a01b03168083526006546001600160801b038082166020860152600160801b909104168385015283519182018085526306fdde0360e01b90529251939a50909850918291906306fdde03906064808501915f918187030181865afa15801561102d573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611054919081019061486c565b815260200160055f015f9054906101000a90046001600160a01b03166001600160a01b03166395d89b416040518163ffffffff1660e01b81526004015f60405180830381865afa1580156110aa573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526110d1919081019061486c565b81526005546040805163313ce56760e01b815290516020938401936001600160a01b039093169263313ce56792600480820193918290030181865afa15801561111c573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061114091906149b9565b60ff16815250945060076040518060400160405290815f820180546111649061471f565b80601f01602080910402602001604051908101604052809291908181526020018280546111909061471f565b80156111db5780601f106111b2576101008083540402835291602001916111db565b820191905f5260205f20905b8154815290600101906020018083116111be57829003601f168201915b5050505050815260200160018201805480602002602001604051908101604052809291908181526020015f905b82821015611388575f8481526020908190206040805160a08101825260048602909201805460ff8116845261010090046001600160f81b0316938301939093526001830154908201526002820180549192916060840191906112699061471f565b80601f01602080910402602001604051908101604052809291908181526020018280546112959061471f565b80156112e05780601f106112b7576101008083540402835291602001916112e0565b820191905f5260205f20905b8154815290600101906020018083116112c357829003601f168201915b505050505081526020016003820180546112f99061471f565b80601f01602080910402602001604051908101604052809291908181526020018280546113259061471f565b80156113705780601f1061134757610100808354040283529160200191611370565b820191905f5260205f20905b81548152906001019060200180831161135357829003601f168201915b50505050508152505081526020019060010190611208565b5050509152505060098054604080516020808402820181019092528281529397505f9084015b828210156114d2578382905f5260205f2090600202016040518060800160405290815f82015f9054906101000a900460ff1660028111156113f1576113f16140b8565b6002811115611402576114026140b8565b8152815461010081046001600160581b03166020830152600160601b90046001600160a01b031660408201526001820180546060909201916114439061471f565b80601f016020809104026020016040519081016040528092919081815260200182805461146f9061471f565b80156114ba5780601f10611491576101008083540402835291602001916114ba565b820191905f5260205f20905b81548152906001019060200180831161149d57829003601f168201915b505050505081525050815260200190600101906113ae565b5050505092505f5f6114e2611d38565b6040516310cf02b960e01b81523060048201526001600160a01b0391909116906310cf02b9906024016040805180830381865afa158015611525573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061154991906149d4565b915091506040518060c001604052805f60039054906101000a90046001600160e81b03166001600160e81b031681526020016002548152602001600154815260200183815260200182815260200161159f61044a565b905293506115ab612bf9565b925050509091929394959697565b6115c1613163565b6001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115fc573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061162091906148e0565b6001600160a01b0316336001600160a01b031614611651576040516315cead3960e31b815260040160405180910390fd5b600161165b612bf9565b600581111561166c5761166c6140b8565b1461169357611679612bf9565b60405163a3acd01960e01b8152600401610ca491906146e6565b5f61169c61318d565b6001600160801b03169050805f60065460408051600160801b9092046001600160801b0316825230602083015233917f4bf986268e3c9b6b83f7e5fc1afcd134e539e7ab3188bd6e1a2cbe07ed9837a19101610d8b565b5f61083b60706133d2565b611706613163565b6001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa158015611741573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061176591906148e0565b6001600160a01b0316336001600160a01b031614611796576040516315cead3960e31b815260040160405180910390fd5b60408051336024820152604480820184905282518083039091018152606490910182526020810180516001600160e01b031663a9059cbb60e01b17905290515f9182916001600160a01b038616916117ed916149f6565b5f604051808303815f865af19150503d805f8114611826576040519150601f19603f3d011682016040523d82523d5f602084013e61182b565b606091505b509150915081801561185557508051158061185557508080602001905181019061185591906148fb565b6118a15760405162461bcd60e51b815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c4544006044820152606401610ca4565b50505050565b604080518082019091526060808252602082015260076040518060400160405290815f820180546118d79061471f565b80601f01602080910402602001604051908101604052809291908181526020018280546119039061471f565b801561194e5780601f106119255761010080835404028352916020019161194e565b820191905f5260205f20905b81548152906001019060200180831161193157829003601f168201915b5050505050815260200160018201805480602002602001604051908101604052809291908181526020015f905b82821015611afb575f8481526020908190206040805160a08101825260048602909201805460ff8116845261010090046001600160f81b0316938301939093526001830154908201526002820180549192916060840191906119dc9061471f565b80601f0160208091040260200160405190810160405280929190818152602001828054611a089061471f565b8015611a535780601f10611a2a57610100808354040283529160200191611a53565b820191905f5260205f20905b815481529060010190602001808311611a3657829003601f168201915b50505050508152602001600382018054611a6c9061471f565b80601f0160208091040260200160405190810160405280929190818152602001828054611a989061471f565b8015611ae35780601f10611aba57610100808354040283529160200191611ae3565b820191905f5260205f20905b815481529060010190602001808311611ac657829003601f168201915b5050505050815250508152602001906001019061197b565b5050505081525050905090565b5f61083b6028613169565b6001611b1d612bf9565b6005811115611b2e57611b2e6140b8565b14611b3b57611679612bf9565b5f819003611b5c5760405163b931f4df60e01b815260040160405180910390fd5b5f611b6561044a565b90508015611c7b577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166323b872dd33611ba5613163565b6001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa158015611be0573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c0491906148e0565b6040516001600160e01b031960e085901b1681526001600160a01b03928316600482015291166024820152604481018490526064016020604051808303815f875af1158015611c55573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c7991906148fb565b505b5f8054630100000090046001600160e81b0316906003611c9a83614a01565b91906101000a8154816001600160e81b0302191690836001600160e81b0316021790555050611cc7611d38565b60065460405163603ab8b760e01b81526001600160a01b03929092169163603ab8b791611d0691879187916001600160801b0390911690600401614a54565b5f604051808303815f87803b158015611d1d575f5ffd5b505af1158015611d2f573d5f5f3e3d5ffd5b50505050505050565b5f611d416125c1565b6001600160a01b0316639b5e6a8b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d7c573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061083b91906148e0565b5f5f5f5f611dac611d38565b6001600160a01b0316336001600160a01b031614611ddd576040516327a891ff60e01b815260040160405180910390fd5b600b8888604051611def929190614a80565b9081526020016040518091039020545f03611e1d5760028054905f611e1383614a8f565b9190505550600190505b8415611e75578560015f828254611e349190614aa7565b9250508190555085600b8989604051611e4e929190614a80565b90815260200160405180910390205f828254611e6a9190614aa7565b90915550611f0c9050565b8560015f828254611e86919061492a565b9250508190555085600b8989604051611ea0929190614a80565b90815260200160405180910390205f828254611ebc919061492a565b9091555050604051600b90611ed4908a908a90614a80565b9081526020016040518091039020545f03611f0c5760028054905f611ef883614aba565b91905055508080611f0890614acf565b9150505b6001549350600b8888604051611f23929190614a80565b90815260200160405180910390205492506002549150945094509450949050565b5f831580611f50575081155b80611f5d57506009548414155b80611f6a57506008548214155b15611f8857604051630ae8117760e21b815260040160405180910390fd5b5f670de0b6b3a764000060608288888281611fa557611fa56146f4565b9050602002810190611fb79190614aec565b611fc5906020810190614b16565b6002811115611fd657611fd66140b8565b03611fff5750604080518082019091526007815266028a096a0a49d160cd1b602082015261208b565b6001888885818110612013576120136146f4565b90506020028101906120259190614aec565b612033906020810190614b16565b6002811115612044576120446140b8565b0361206c5750604080518082019091526006815265028a096a19d160d51b602082015261208b565b50604080518082019091526006815265028a096ab1d160d51b60208201525b86831015612273578787848181106120a5576120a56146f4565b90506020028101906120b79190614aec565b6120c8906040810190602001614b45565b6001600160581b03161561227357600a818989868181106120eb576120eb6146f4565b90506020028101906120fd9190614aec565b61210b906060810190614b60565b60405160200161211d93929190614ba2565b60408051601f1981840301815290829052612137916149f6565b9081526040519081900360200190205460ff166121ba5780888885818110612161576121616146f4565b90506020028101906121739190614aec565b612181906060810190614b60565b60405160200161219393929190614ba2565b60408051601f1981840301815290829052633cfbd52760e21b8252610ca491600401613fc3565b61226661225f612211600986815481106121d6576121d66146f4565b5f9182526020909120600290910201546122059061010090046001600160581b0316662386f26fc10000614bc0565b6001600160581b031690565b6122598b8b88818110612226576122266146f4565b90506020028101906122389190614aec565b612249906040810190602001614b45565b612205906509184e72a000614bc0565b906133f3565b8390613500565b600190930192915061208b565b828714612285575f93505050506125b9565b6040515f935061229a9060079060200161484f565b60405160208183030381529060405290505f6122b35f90565b90505b858410156124fa57600a828888878181106122d3576122d36146f4565b90506020028101906122e59190614be9565b6122f3906060810190614b60565b60405160200161230593929190614ba2565b60408051601f198184030181529082905261231f916149f6565b9081526040519081900360200190205460ff166123a25781878786818110612349576123496146f4565b905060200281019061235b9190614be9565b612369906060810190614b60565b60405160200161237b93929190614ba2565b60408051601f1981840301815290829052631f3f577f60e21b8252610ca491600401613fc3565b8686858181106123b4576123b46146f4565b90506020028101906123c69190614be9565b6123d7906040810190602001614bfd565b6001600160f81b0316600760010185815481106123f6576123f66146f4565b5f91825260209091206004909102015461010090046001600160f81b0316111561242157505f6124fa565b6124ed6124e66124a061245660076001018881548110612443576124436146f4565b905f5260205f2090600402016001015490565b61249a8b8b8a81811061246b5761246b6146f4565b905060200281019061247d9190614be9565b61248e906040810190602001614bfd565b6001600160f81b031690565b90613515565b6124e0600760010188815481106124b9576124b96146f4565b5f9182526020909120600490910201546124dd9060ff16662386f26fc10000614c18565b90565b90613500565b829061352c565b60019094019390506122b6565b83861461250d575f9450505050506125b9565b5f61251b6124dd8584613500565b90505f8160015461252c9190614aa7565b61253983620f4240614c18565b6125439190614c43565b6006549091505f90620f42409061256b908490600160801b90046001600160801b0316614c18565b6125759190614c43565b6006549091506001600160801b03161580159061259d57506006546001600160801b03168110155b156125b057506006546001600160801b03165b96505050505050505b949350505050565b5f61083b6014613169565b5f5f5f6125d7611d38565b6001600160a01b031663aa8ff04e3087876040518463ffffffff1660e01b815260040161260693929190614c62565b608060405180830381865afa158015612621573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906126459190614cbc565b935050509150816126695760405163b931f4df60e01b815260040160405180910390fd5b9150505b92915050565b61267b6125c1565b6001600160a01b0316336001600160a01b0316146126ac5760405163cf7527d960e01b815260040160405180910390fd5b604051600b60fb1b602082015260210160408051601f1981840301815291905280516020909101206126de8480614b60565b6040516020016126ef929190614a80565b6040516020818303038152906040528051906020012003612723576040516302448d4360e31b815260040160405180910390fd5b6004612730868883614d4f565b5083600561273e8282614e1f565b50839050600761274e828261507c565b9050505f60605b82821015612a14576009848484818110612771576127716146f4565b90506020028101906127839190614aec565b81546001810183555f92835260209092209091600202016127a48282615158565b505f90508484848181106127ba576127ba6146f4565b90506020028101906127cc9190614aec565b6127da906020810190614b16565b60028111156127eb576127eb6140b8565b036128145750604080518082019091526007815266028a096a0a49d160cd1b60208201526128a0565b6001848484818110612828576128286146f4565b905060200281019061283a9190614aec565b612848906020810190614b16565b6002811115612859576128596140b8565b036128815750604080518082019091526006815265028a096a19d160d51b60208201526128a0565b50604080518082019091526006815265028a096ab1d160d51b60208201525b600a818585858181106128b5576128b56146f4565b90506020028101906128c79190614aec565b6128d5906060810190614b60565b6040516020016128e793929190614ba2565b60408051601f1981840301815290829052612901916149f6565b9081526040519081900360200190205460ff1615612985578084848481811061292c5761292c6146f4565b905060200281019061293e9190614aec565b61294c906060810190614b60565b60405160200161295e93929190614ba2565b60408051601f1981840301815290829052634c368ddf60e11b8252610ca491600401613fc3565b6001600a8286868681811061299c5761299c6146f4565b90506020028101906129ae9190614aec565b6129bc906060810190614b60565b6040516020016129ce93929190614ba2565b60408051601f19818403018152908290526129e8916149f6565b908152604051908190036020019020805491151560ff1990921691909117905560019190910190612755565b5f9150612a218580614b60565b604051602001612a3292919061520b565b60405160208183030381529060405290505b612a516020860186614e87565b9050821015612bef57600a81612a6a6020880188614e87565b85818110612a7a57612a7a6146f4565b9050602002810190612a8c9190614be9565b612a9a906060810190614b60565b604051602001612aac93929190614ba2565b60408051601f1981840301815290829052612ac6916149f6565b9081526040519081900360200190205460ff1615612b555780612aec6020870187614e87565b84818110612afc57612afc6146f4565b9050602002810190612b0e9190614be9565b612b1c906060810190614b60565b604051602001612b2e93929190614ba2565b60408051601f19818403018152908290526307bd843d60e41b8252610ca491600401613fc3565b6001600a82612b676020890189614e87565b86818110612b7757612b776146f4565b9050602002810190612b899190614be9565b612b97906060810190614b60565b604051602001612ba993929190614ba2565b60408051601f1981840301815290829052612bc3916149f6565b908152604051908190036020019020805491151560ff1990921691909117905560019190910190612a44565b5050505050505050565b5f60045f5460ff166005811115612c1257612c126140b8565b1480612c33575060055f5460ff166005811115612c3157612c316140b8565b145b15612c4157505f5460ff1690565b612c496116f3565b4211612c5457505f90565b612c5c6116f3565b42118015612c715750612c6d610c31565b4211155b15612c92575f5462010000900460ff1615612c8c5750600290565b50600190565b50600390565b5f612ca360506133d2565b919050565b60045f5460ff166005811115612cc057612cc06140b8565b14612cde576040516306d27d2160e11b815260040160405180910390fd5b5f5f612ce8611d38565b6001600160a01b031663aa8ff04e3086866040518463ffffffff1660e01b8152600401612d1793929190614c62565b608060405180830381865afa158015612d32573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d569190614cbc565b93505050915081612d7a5760405163b931f4df60e01b815260040160405180910390fd5b805f03612d9a5760405163aebd15cf60e01b815260040160405180910390fd5b600b8484604051612dac929190614a80565b9081526020016040518091039020545f03612dda57604051630169d19960e31b815260040160405180910390fd5b5f600154600b8686604051612df0929190614a80565b908152602001604051809103902054620f4240612e0d9190614c18565b612e179190614c43565b6006549091505f90620f424090612e3f908490600160801b90046001600160801b0316614c18565b612e499190614c43565b6006549091505f906001600160801b03168103612e9257600254600103612e8d57600354600654612e8a9190600160801b90046001600160801b031661492a565b91505b612fe6565b6006546001600160801b03168210612eb3576006546001600160801b031691505b600254600114612f02575f54610100900460ff16158015612ede57506006546001600160801b031682105b15612efd57600654612efa9083906001600160801b031661492a565b90505b612f57565b600354600654612f229190600160801b90046001600160801b031661492a565b821015612f57576003546006548391612f4a91600160801b90046001600160801b031661492a565b612f54919061492a565b90505b8015612fe6576005546001600160a01b031663a9059cbb612f76610840565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602481018490526044016020604051808303815f875af1158015612fc0573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612fe491906148fb565b505b60055460405163a9059cbb60e01b81526001600160a01b038681166004830152602482018590529091169063a9059cbb906044016020604051808303815f875af1158015613036573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061305a91906148fb565b50600b878760405161306d929190614a80565b9081526040519081900360200190205f90819055600280549161308f83614aba565b9091555061309f90508183614aa7565b60035f8282546130af9190614aa7565b925050819055508082856001600160a01b03167f17bd1e02fad55c41d40ef34cebf779ffad5f6d531a597f3051e234e74c2c630e6003548b8b306040516130f99493929190615222565b60405180910390a46002545f03611d2f575f805460ff19166005179055600680546001600160801b0316905560405130815242907f4b458c68578d57188b55fc98cef8757c807194fb31378b88df039807cc5159029060200160405180910390a250505050505050565b5f61083b5f5b5f5f61317f600119368181013560f01c90030190565b929092013560601c92915050565b5f805460ff1916600417815560065481906001600160801b0316158015906131d657506006546002546001600160801b03600160801b83048116926131d492911690614c18565b105b156132f4576006546002546131f4916001600160801b031690614c18565b6006546132119190600160801b90046001600160801b031661492a565b6005549091506001600160a01b031663a9059cbb61322d610840565b6040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526001600160801b03841660248201526044016020604051808303815f875af115801561327f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132a391906148fb565b50600680548291906010906132c9908490600160801b90046001600160801b0316615254565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508091505090565b6006546001600160801b031615801561330d5750600254155b156133bf576005546001600160a01b031663a9059cbb61332b611b08565b60065460405160e084901b6001600160e01b03191681526001600160a01b039092166004830152600160801b90046001600160801b031660248201526044016020604051808303815f875af1158015613386573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906133aa91906148fb565b50600680546001600160801b03169055919050565b5f805461ff001916610100179055919050565b5f5f6133e8600119368181013560f01c90030190565b929092013592915050565b5f828281830361341c578015613409575f613413565b670de0b6b3a76400005b9250505061266d565b670de0b6b3a7640000820361343d57670de0b6b3a76400009250505061266d565b805f0361345657670de0b6b3a76400009250505061266d565b670de0b6b3a7640000810361346f57849250505061266d565b670de0b6b3a76400008211156134a05761349961349461348e8761353a565b86613500565b613661565b92506134f8565b5f6134bd6124dd846ec097ce7bc90715b34b9f1000000000614c43565b90505f6134d56134946134cf8461353a565b88613500565b90506134f36124dd826ec097ce7bc90715b34b9f1000000000614c43565b945050505b505092915050565b5f61350e6124dd84846136b5565b9392505050565b5f61350e6124dd84670de0b6b3a764000085613767565b5f61350e6124dd8385614aa7565b5f81670de0b6b3a76400008110156135685760405163036d32ef60e41b815260048101849052602401610ca4565b5f6135e9670de0b6b3a7640000830460016001600160801b03821160071b91821c6001600160401b03811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c60ff8111600390811b91821c600f811160021b90811c918211871b91821c969096119490961792909217171791909117919091171790565b9050670de0b6b3a7640000810282821c670de0b6b3a763ffff1981016136125750949350505050565b671bc16d674ec800006706f05b59d3b200005b801561365557670de0b6b3a764000083800204925081831061364d579283019260019290921c915b60011c613625565b50919695505050505050565b5f81680a688906bd8affffff8111156136905760405163b3b6ba1f60e01b815260048101849052602401610ca4565b5f6136a7670de0b6b3a7640000604084901b614c43565b90506125b96124dd82613835565b5f80805f19848609848602925082811083820303915050805f036136e65750670de0b6b3a76400009004905061266d565b670de0b6b3a7640000811061371857604051635173648d60e01b81526004810186905260248101859052604401610ca4565b5f670de0b6b3a764000085870962040000818503049310909103600160ee1b02919091177faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106690291505092915050565b5f80805f19858709858702925082811083820303915050805f0361379e5783828161379457613794614c2f565b049250505061350e565b8381106137cf57604051630c740aef60e31b8152600481018790526024810186905260448101859052606401610ca4565b5f84868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203025f889003889004909101858311909403939093029303949094049190911702949350505050565b600160bf1b67ff00000000000000821615613942576780000000000000008216156138695768016a09e667f3bcc9090260401c5b674000000000000000821615613888576801306fe0a31b7152df0260401c5b6720000000000000008216156138a7576801172b83c7d517adce0260401c5b6710000000000000008216156138c65768010b5586cf9890f62a0260401c5b6708000000000000008216156138e5576801059b0d31585743ae0260401c5b67040000000000000082161561390457680102c9a3e778060ee70260401c5b6702000000000000008216156139235768010163da9fb33356d80260401c5b67010000000000000082161561394257680100b1afa5abcbed610260401c5b66ff000000000000821615613a4157668000000000000082161561396f5768010058c86da1c09ea20260401c5b664000000000000082161561398d576801002c605e2e8cec500260401c5b66200000000000008216156139ab57680100162f3904051fa10260401c5b66100000000000008216156139c9576801000b175effdc76ba0260401c5b66080000000000008216156139e757680100058ba01fb9f96d0260401c5b6604000000000000821615613a055768010002c5cc37da94920260401c5b6602000000000000821615613a23576801000162e525ee05470260401c5b6601000000000000821615613a415768010000b17255775c040260401c5b65ff0000000000821615613b375765800000000000821615613a6c576801000058b91b5bc9ae0260401c5b65400000000000821615613a8957680100002c5c89d5ec6d0260401c5b65200000000000821615613aa65768010000162e43f4f8310260401c5b65100000000000821615613ac357680100000b1721bcfc9a0260401c5b65080000000000821615613ae05768010000058b90cf1e6e0260401c5b65040000000000821615613afd576801000002c5c863b73f0260401c5b65020000000000821615613b1a57680100000162e430e5a20260401c5b65010000000000821615613b37576801000000b1721835510260401c5b64ff00000000821615613c2457648000000000821615613b6057680100000058b90c0b490260401c5b644000000000821615613b7c5768010000002c5c8601cc0260401c5b642000000000821615613b98576801000000162e42fff00260401c5b641000000000821615613bb45768010000000b17217fbb0260401c5b640800000000821615613bd0576801000000058b90bfce0260401c5b640400000000821615613bec57680100000002c5c85fe30260401c5b640200000000821615613c085768010000000162e42ff10260401c5b640100000000821615613c2457680100000000b17217f80260401c5b63ff000000821615613d08576380000000821615613c4b5768010000000058b90bfc0260401c5b6340000000821615613c66576801000000002c5c85fe0260401c5b6320000000821615613c8157680100000000162e42ff0260401c5b6310000000821615613c9c576801000000000b17217f0260401c5b6308000000821615613cb757680100000000058b90c00260401c5b6304000000821615613cd25768010000000002c5c8600260401c5b6302000000821615613ced576801000000000162e4300260401c5b6301000000821615613d085768010000000000b172180260401c5b62ff0000821615613de35762800000821615613d2d576801000000000058b90c0260401c5b62400000821615613d4757680100000000002c5c860260401c5b62200000821615613d615768010000000000162e430260401c5b62100000821615613d7b57680100000000000b17210260401c5b62080000821615613d955768010000000000058b910260401c5b62040000821615613daf576801000000000002c5c80260401c5b62020000821615613dc957680100000000000162e40260401c5b62010000821615613de3576801000000000000b1720260401c5b61ff00821615613eb557618000821615613e0657680100000000000058b90260401c5b614000821615613e1f5768010000000000002c5d0260401c5b612000821615613e38576801000000000000162e0260401c5b611000821615613e515768010000000000000b170260401c5b610800821615613e6a576801000000000000058c0260401c5b610400821615613e8357680100000000000002c60260401c5b610200821615613e9c57680100000000000001630260401c5b610100821615613eb557680100000000000000b10260401c5b60ff821615613f7e576080821615613ed657680100000000000000590260401c5b6040821615613eee576801000000000000002c0260401c5b6020821615613f0657680100000000000000160260401c5b6010821615613f1e576801000000000000000b0260401c5b6008821615613f3657680100000000000000060260401c5b6004821615613f4e57680100000000000000030260401c5b6002821615613f6657680100000000000000010260401c5b6001821615613f7e57680100000000000000010260401c5b670de0b6b3a76400000260409190911c60bf031c90565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f61350e6020830184613f95565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b038111828210171561401157614011613fd5565b604052919050565b5f6001600160401b0382111561403157614031613fd5565b50601f01601f191660200190565b5f6020828403121561404f575f5ffd5b81356001600160401b03811115614064575f5ffd5b8201601f81018413614074575f5ffd5b803561408761408282614019565b613fe9565b81815285602083850101111561409b575f5ffd5b816020840160208301375f91810160200191909152949350505050565b634e487b7160e01b5f52602160045260245ffd5b5f82825180855260208501945060208160051b830101602085015f5b8381101561416757848303601f19018852815180516003811061410d5761410d6140b8565b84526020818101516001600160581b0316908501526040808201516001600160a01b03169085015260609081015160809185018290529061415090850182613f95565b6020998a01999094509290920191506001016140e8565b50909695505050505050565b602081525f61350e60208301846140cc565b80516001600160a01b031682526020808201516001600160801b039081169184019190915260409182015116910152565b6060810161266d8284614185565b5f602082840312156141d4575f5ffd5b5035919050565b5f8151606084526141ef6060850182613f95565b9050602083015184820360208601526142088282613f95565b915050604083015160408501528091505092915050565b5f8151604084526142336040850182613f95565b60208481015186830387830152805180845292935081019181840191600582901b8501015f5b828110156142da57601f19868303018452845160ff815116835260018060f81b03602082015116602084015260408101516040840152606081015160a060608501526142a860a0850182613f95565b90506080820151915083810360808501526142c38183613f95565b602097880197969096019593505050600101614259565b50979650505050505050565b600681106142f6576142f66140b8565b9052565b61022081525f895160a06102208401526143186102c0840182613f95565b905060208b015161021f19848303016102408501526143378282613f95565b60408d01516001600160a01b031661026086015260608d015161028086015260808d01516102a0860152915061439e9050602084018b80516001600160401b031682526020808201516001600160a01b039081169184019190915260409182015116910152565b6143ab608084018a614185565b82810360e08401526143bd81896141db565b90508281036101008401526143d2818861421f565b90508281036101208401526143e781876140cc565b85516101408501526020860151610160850152604086015161018085015260608601516101a085015260808601516101c085015260a08601516101e0850152915061442f9050565b61443d6102008301846142e6565b9998505050505050505050565b6001600160a01b038116811461445e575f5ffd5b50565b5f5f60408385031215614472575f5ffd5b823561447d8161444a565b946020939093013593505050565b602081525f61350e602083018461421f565b5f5f83601f8401126144ad575f5ffd5b5081356001600160401b038111156144c3575f5ffd5b6020830191508360208285010111156144da575f5ffd5b9250929050565b5f5f602083850312156144f2575f5ffd5b82356001600160401b03811115614507575f5ffd5b6145138582860161449d565b90969095509350505050565b801515811461445e575f5ffd5b5f5f5f5f6060858703121561453f575f5ffd5b84356001600160401b03811115614554575f5ffd5b6145608782880161449d565b90955093505060208501359150604085013561457b8161451f565b939692955090935050565b5f5f83601f840112614596575f5ffd5b5081356001600160401b038111156145ac575f5ffd5b6020830191508360208260051b85010111156144da575f5ffd5b5f5f5f5f604085870312156145d9575f5ffd5b84356001600160401b038111156145ee575f5ffd5b6145fa87828801614586565b90955093505060208501356001600160401b03811115614618575f5ffd5b61462487828801614586565b95989497509550505050565b5f5f5f5f5f5f86880360c0811215614646575f5ffd5b87356001600160401b0381111561465b575f5ffd5b6146678a828b0161449d565b9098509650506060601f198201121561467e575f5ffd5b5060208701935060808701356001600160401b0381111561469d575f5ffd5b87016040818a0312156146ae575f5ffd5b925060a08701356001600160401b038111156146c8575f5ffd5b6146d489828a01614586565b979a9699509497509295939492505050565b6020810161266d82846142e6565b634e487b7160e01b5f52603260045260245ffd5b5f81518060208401855e5f93019283525090919050565b600181811c9082168061473357607f821691505b60208210810361475157634e487b7160e01b5f52602260045260245ffd5b50919050565b5f81546147638161471f565b60018216801561477a576001811461478f576147bc565b60ff19831686528115158202860193506147bc565b845f5260205f205f5b838110156147b457815488820152600190910190602001614798565b505081860193505b50505092915050565b5f6125b96147d38386614708565b84614757565b60ff8116811461445e575f5ffd5b6001600160f81b038116811461445e575f5ffd5b5f5f5f5f6080858703121561480e575f5ffd5b84516004811061481c575f5ffd5b602086015190945061482d816147d9565b604086015190935061483e816147e7565b606086015190925061457b8161444a565b5f61485a8284614757565b6101d160f51b81526002019392505050565b5f6020828403121561487c575f5ffd5b81516001600160401b03811115614891575f5ffd5b8201601f810184136148a1575f5ffd5b80516148af61408282614019565b8181528560208385010111156148c3575f5ffd5b8160208401602083015e5f91810160200191909152949350505050565b5f602082840312156148f0575f5ffd5b815161350e8161444a565b5f6020828403121561490b575f5ffd5b815161350e8161451f565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561266d5761266d614916565b5f606082840312801561494e575f5ffd5b50604051606081016001600160401b038111828210171561497157614971613fd5565b60405282516001600160401b038116811461498a575f5ffd5b8152602083015161499a8161444a565b602082015260408301516149ad8161444a565b60408201529392505050565b5f602082840312156149c9575f5ffd5b815161350e816147d9565b5f5f604083850312156149e5575f5ffd5b505080516020909101519092909150565b5f61350e8284614708565b5f6001600160e81b0382166002600160e81b03198101614a2357614a23614916565b60010192915050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b604081525f614a67604083018587614a2c565b90506001600160801b0383166020830152949350505050565b818382375f9101908152919050565b5f60018201614aa057614aa0614916565b5060010190565b8082018082111561266d5761266d614916565b5f81614ac857614ac8614916565b505f190190565b5f81810b60808101614ae357614ae3614916565b5f190192915050565b5f8235607e19833603018112614b00575f5ffd5b9190910192915050565b6003811061445e575f5ffd5b5f60208284031215614b26575f5ffd5b813561350e81614b0a565b6001600160581b038116811461445e575f5ffd5b5f60208284031215614b55575f5ffd5b813561350e81614b31565b5f5f8335601e19843603018112614b75575f5ffd5b8301803591506001600160401b03821115614b8e575f5ffd5b6020019150368190038213156144da575f5ffd5b5f614bad8286614708565b838582375f930192835250909392505050565b6001600160581b038181168382160290811690818114614be257614be2614916565b5092915050565b5f8235609e19833603018112614b00575f5ffd5b5f60208284031215614c0d575f5ffd5b813561350e816147e7565b808202811582820484141761266d5761266d614916565b634e487b7160e01b5f52601260045260245ffd5b5f82614c5d57634e487b7160e01b5f52601260045260245ffd5b500490565b6001600160a01b03841681526060602082018190525f90614c869083018486614a2c565b828103604093840152601481527310d3d3919251ce88105d5d1a1bdc95d85b1b195d60621b602082015291909101949350505050565b5f5f5f5f60808587031215614ccf575f5ffd5b8451614cda8161451f565b60208601516040870151606090970151919890975090945092505050565b5b81811015614d0c575f8155600101614cf9565b5050565b601f821115614d4a57805f5260205f20601f840160051c81016020851015614d355750805b614d47601f850160051c830182614cf8565b50505b505050565b6001600160401b03831115614d6657614d66613fd5565b614d7a83614d74835461471f565b83614d10565b5f601f841160018114614dab575f8515614d945750838201355b5f19600387901b1c1916600186901b178355614d47565b5f83815260208120601f198716915b82811015614dda5786850135825560209485019460019092019101614dba565b5086821015614df6575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b5f81356001600160801b038116811461266d575f5ffd5b8135614e2a8161444a565b81546001600160a01b0319166001600160a01b03919091161781556001600160801b03614e5960208401614e08565b166fffffffffffffffffffffffffffffffff19614e7860408501614e08565b60801b16176001919091015550565b5f5f8335601e19843603018112614e9c575f5ffd5b8301803591506001600160401b03821115614eb5575f5ffd5b6020019150600581901b36038213156144da575f5ffd5b8054614ed79061471f565b8015614d0c57601f811160018114614ef05750505f9055565b5f83815260209020614f0d601f840160051c820160018301614cf8565b505f8381526020812081855555505050565b8135614f2a816147d9565b60ff811690508060ff198354161782556020830135614f48816147e7565b60081b60ff191617815560408201356001820155614f696060830183614b60565b614f77818360028601614d4f565b5050614f866080830183614b60565b6118a1818360038601614d4f565b68010000000000000000831115614fad57614fad613fd5565b805483825580841015615037576001600160fe1b0381168114614fd257614fd2614916565b6001600160fe1b0384168414614fea57614fea614916565b815f5260205f208160021b81018560021b820191505b80821015615034575f82555f600183015561501d60028301614ecc565b61502960038301614ecc565b600482019150615000565b50505b505f8181526020812083915b858110156150745761505e6150588487614be9565b83614f1f565b6020929092019160049190910190600101615043565b505050505050565b6150868283614b60565b6001600160401b0381111561509d5761509d613fd5565b6150b1816150ab855461471f565b85614d10565b5f601f8211600181146150e2575f83156150cb5750838201355b5f19600385901b1c1916600184901b178555615139565b5f85815260208120601f198516915b8281101561511157868501358255602094850194600190920191016150f1565b508482101561512d575f1960f88660031b161c19848701351681555b505060018360011b0185555b5050505061514a6020830183614e87565b6118a1818360018601614f94565b813561516381614b0a565b60038110615173576151736140b8565b815460ff821691508160ff198216178355602084013561519281614b31565b6bffffffffffffffffffffff008160081b16836bffffffffffffffffffffffff1984161717845550505060408201356151ca8161444a565b81546bffffffffffffffffffffffff16606091821b6bffffffffffffffffffffffff19161782556151fd90830183614b60565b6118a1818360018601614d4f565b818382376101d160f51b9101908152600201919050565b848152606060208201525f61523b606083018587614a2c565b905060018060a01b038316604083015295945050505050565b6001600160801b03828116828216039081111561266d5761266d61491656fea2646970667358221220bde31421032b967b56a78e8b89516ec194451095a77a79c0e8ebe5c6cf47211064736f6c634300081e0033
Deployed Bytecode
0x608060405234801561000f575f5ffd5b50600436106101bb575f3560e01c80638c7a8eb3116100f3578063c45a015511610093578063ef8a92351161006e578063ef8a923514610412578063f172a4ce14610427578063f3fe12c91461042f578063f851a44014610442575f5ffd5b8063c45a0155146103e4578063cdcb3942146103ec578063e8009948146103ff575f5ffd5b8063955c9867116100ce578063955c986714610356578063a7c64c701461035e578063aa588b4b14610393578063b7701e44146103d1575f5ffd5b80638c7a8eb3146103265780638da5cb5b1461033b578063951f823914610343575f5ffd5b80633197cbb61161015e578063565974d311610139578063565974d3146102e757806373e4fc0b1461030357806378e979251461030b5780638980f11f14610313575f5ffd5b80633197cbb6146102615780633326ee9c1461026957806349c26fcb146102d4575f5ffd5b80630ebf14b5116101995780630ebf14b51461020f5780631f55479f1461021957806324034a0e1461022157806326f016371461024c575f5ffd5b806304cbb47f146101bf57806306fdde03146101da5780630cb61f6c146101ef575b5f5ffd5b6101c761044a565b6040519081526020015b60405180910390f35b6101e26107bc565b6040516101d19190613fc3565b6101f7610840565b6040516001600160a01b0390911681526020016101d1565b61021761084b565b005b610217610943565b6101c761022f36600461403f565b8051602081830181018051600b8252928201919093012091525481565b610254610ae1565b6040516101d19190614173565b6101c7610c31565b6102c7604080516060810182525f808252602082018190529181019190915250604080516060810182526005546001600160a01b031681526006546001600160801b038082166020840152600160801b909104169181019190915290565b6040516101d191906141b6565b6102176102e23660046141c4565b610c3c565b6102ef610d96565b6040516101d19897969594939291906142fa565b6102176115b9565b6101c76116f3565b610217610321366004614461565b6116fe565b61032e6118a7565b6040516101d1919061448b565b6101f7611b08565b6102176103513660046144e1565b611b13565b6101f7611d38565b61037161036c36600461452c565b611da0565b604080519485526020850193909352918301525f0b60608201526080016101d1565b6103c16103a136600461403f565b8051602081830181018051600a8252928201919093012091525460ff1681565b60405190151581526020016101d1565b6101c76103df3660046145c6565b611f44565b6101f76125c1565b6101f76103fa3660046144e1565b6125cc565b61021761040d366004614630565b612673565b61041a612bf9565b6040516101d191906146e6565b6101c7612c98565b61021761043d3660046144e1565b612ca8565b6101f7613163565b6009545f9081808060605b84841015610675575f60098581548110610471576104716146f4565b5f91825260209091206002918202015460ff1690811115610494576104946140b8565b036104bd5750604080518082019091526007815266028a096a0a49d160cd1b602082015261053c565b6001600985815481106104d2576104d26146f4565b5f91825260209091206002918202015460ff16908111156104f5576104f56140b8565b0361051d5750604080518082019091526006815265028a096a19d160d51b602082015261053c565b50604080518082019091526006815265028a096ab1d160d51b60208201525b610544613163565b6001600160a01b031663b6001e2460098681548110610565576105656146f4565b905f5260205f2090600202015f01600c9054906101000a90046001600160a01b0316836009888154811061059b5761059b6146f4565b905f5260205f2090600202016001016040516020016105bb9291906147c5565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b81526004016106029291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa15801561061d573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061064191906147fb565b506001600160f81b0316935050821590508061065d5750816001145b15610666575f91505b60019093019291810191610455565b6008546040519095505f94506106909060079060200161484f565b60405160208183030381529060405290505b848410156107b2576106b2613163565b6001600160a01b031663b6001e245f83600760010188815481106106d8576106d86146f4565b905f5260205f2090600402016002016040516020016106f89291906147c5565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b815260040161073f9291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa15801561075a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061077e91906147fb565b506001600160f81b0316935050821590508061079a5750816001145b156107a3575f91505b600190930192918101916106a2565b5090949350505050565b60606107c66125c1565b6001600160a01b031663fe4d55366107dc612c98565b6040518263ffffffff1660e01b81526004016107fa91815260200190565b5f60405180830381865afa158015610814573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261083b919081019061486c565b905090565b5f61083b603c613169565b610853613163565b6001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa15801561088e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108b291906148e0565b6001600160a01b0316336001600160a01b0316146108e3576040516315cead3960e31b815260040160405180910390fd5b5f805460ff62010000808304821615810262ff0000199093169290921792839055604051308152919092049091161515907f771cd98ea23fa6204e979f96eecb381b08abb79f1a85d2a4e7b840086feeb86f9060200160405180910390a2565b5f61094c613163565b6001600160a01b031663b6001e246001604051602001610983907021a7a72324a39d102334b730b634bd32b960791b815260110190565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b81526004016109ca9291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa1580156109e5573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a0991906147fb565b9350505050806001600160a01b0316336001600160a01b031614610a40576040516389dd324f60e01b815260040160405180910390fd5b6003610a4a612bf9565b6005811115610a5b57610a5b6140b8565b14610a79576040516380bb6a1760e01b815260040160405180910390fd5b5f610a8261318d565b6001600160801b0316905080600160065460408051600160801b9092046001600160801b0316825230602083015233917f4bf986268e3c9b6b83f7e5fc1afcd134e539e7ab3188bd6e1a2cbe07ed9837a1910160405180910390a45050565b60606009805480602002602001604051908101604052809291908181526020015f905b82821015610c28578382905f5260205f2090600202016040518060800160405290815f82015f9054906101000a900460ff166002811115610b4757610b476140b8565b6002811115610b5857610b586140b8565b8152815461010081046001600160581b03166020830152600160601b90046001600160a01b03166040820152600182018054606090920191610b999061471f565b80601f0160208091040260200160405190810160405280929190818152602001828054610bc59061471f565b8015610c105780601f10610be757610100808354040283529160200191610c10565b820191905f5260205f20905b815481529060010190602001808311610bf357829003601f168201915b50505050508152505081526020019060010190610b04565b50505050905090565b5f61083b60906133d2565b5f610c45612bf9565b6005811115610c5657610c566140b8565b14158015610c7d57506001610c69612bf9565b6005811115610c7a57610c7a6140b8565b14155b15610cad57610c8a612bf9565b60405163eb37a58560e01b8152600401610ca491906146e6565b60405180910390fd5b6005546040516323b872dd60e01b8152336004820152306024820152604481018390526001600160a01b03909116906323b872dd906064016020604051808303815f875af1158015610d01573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d2591906148fb565b50600680546001600160801b03808216600160801b9283900482168501821683021792839055910416610d58828261492a565b6040805133815230602082015284917f3e5931cb7426e4e39150cbb111cc76ae83a5702ccd97fb254c357bcff6b756c591015b60405180910390a450565b610dce6040518060a0016040528060608152602001606081526020015f6001600160a01b031681526020015f81526020015f81525090565b60408051606080820183525f808352602080840182905283850182905284518084018652828152808201839052808601839052855180850187528481529182019390935293840152909160408051808201909152606080825260208201526060610e616040518060c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f6040518060a00160405280610e756107bc565b815260200160048054610e879061471f565b80601f0160208091040260200160405190810160405280929190818152602001828054610eb39061471f565b8015610efe5780601f10610ed557610100808354040283529160200191610efe565b820191905f5260205f20905b815481529060010190602001808311610ee157829003601f168201915b50505050508152602001610f10611b08565b6001600160a01b03168152602001610f266116f3565b8152602001610f33610c31565b90529750610f3f6125c1565b6001600160a01b0316632c960900610f556107bc565b6040518263ffffffff1660e01b8152600401610f719190613fc3565b606060405180830381865afa158015610f8c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fb0919061493d565b60408051606080820183526005546001600160a01b03168083526006546001600160801b038082166020860152600160801b909104168385015283519182018085526306fdde0360e01b90529251939a50909850918291906306fdde03906064808501915f918187030181865afa15801561102d573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611054919081019061486c565b815260200160055f015f9054906101000a90046001600160a01b03166001600160a01b03166395d89b416040518163ffffffff1660e01b81526004015f60405180830381865afa1580156110aa573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526110d1919081019061486c565b81526005546040805163313ce56760e01b815290516020938401936001600160a01b039093169263313ce56792600480820193918290030181865afa15801561111c573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061114091906149b9565b60ff16815250945060076040518060400160405290815f820180546111649061471f565b80601f01602080910402602001604051908101604052809291908181526020018280546111909061471f565b80156111db5780601f106111b2576101008083540402835291602001916111db565b820191905f5260205f20905b8154815290600101906020018083116111be57829003601f168201915b5050505050815260200160018201805480602002602001604051908101604052809291908181526020015f905b82821015611388575f8481526020908190206040805160a08101825260048602909201805460ff8116845261010090046001600160f81b0316938301939093526001830154908201526002820180549192916060840191906112699061471f565b80601f01602080910402602001604051908101604052809291908181526020018280546112959061471f565b80156112e05780601f106112b7576101008083540402835291602001916112e0565b820191905f5260205f20905b8154815290600101906020018083116112c357829003601f168201915b505050505081526020016003820180546112f99061471f565b80601f01602080910402602001604051908101604052809291908181526020018280546113259061471f565b80156113705780601f1061134757610100808354040283529160200191611370565b820191905f5260205f20905b81548152906001019060200180831161135357829003601f168201915b50505050508152505081526020019060010190611208565b5050509152505060098054604080516020808402820181019092528281529397505f9084015b828210156114d2578382905f5260205f2090600202016040518060800160405290815f82015f9054906101000a900460ff1660028111156113f1576113f16140b8565b6002811115611402576114026140b8565b8152815461010081046001600160581b03166020830152600160601b90046001600160a01b031660408201526001820180546060909201916114439061471f565b80601f016020809104026020016040519081016040528092919081815260200182805461146f9061471f565b80156114ba5780601f10611491576101008083540402835291602001916114ba565b820191905f5260205f20905b81548152906001019060200180831161149d57829003601f168201915b505050505081525050815260200190600101906113ae565b5050505092505f5f6114e2611d38565b6040516310cf02b960e01b81523060048201526001600160a01b0391909116906310cf02b9906024016040805180830381865afa158015611525573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061154991906149d4565b915091506040518060c001604052805f60039054906101000a90046001600160e81b03166001600160e81b031681526020016002548152602001600154815260200183815260200182815260200161159f61044a565b905293506115ab612bf9565b925050509091929394959697565b6115c1613163565b6001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115fc573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061162091906148e0565b6001600160a01b0316336001600160a01b031614611651576040516315cead3960e31b815260040160405180910390fd5b600161165b612bf9565b600581111561166c5761166c6140b8565b1461169357611679612bf9565b60405163a3acd01960e01b8152600401610ca491906146e6565b5f61169c61318d565b6001600160801b03169050805f60065460408051600160801b9092046001600160801b0316825230602083015233917f4bf986268e3c9b6b83f7e5fc1afcd134e539e7ab3188bd6e1a2cbe07ed9837a19101610d8b565b5f61083b60706133d2565b611706613163565b6001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa158015611741573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061176591906148e0565b6001600160a01b0316336001600160a01b031614611796576040516315cead3960e31b815260040160405180910390fd5b60408051336024820152604480820184905282518083039091018152606490910182526020810180516001600160e01b031663a9059cbb60e01b17905290515f9182916001600160a01b038616916117ed916149f6565b5f604051808303815f865af19150503d805f8114611826576040519150601f19603f3d011682016040523d82523d5f602084013e61182b565b606091505b509150915081801561185557508051158061185557508080602001905181019061185591906148fb565b6118a15760405162461bcd60e51b815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c4544006044820152606401610ca4565b50505050565b604080518082019091526060808252602082015260076040518060400160405290815f820180546118d79061471f565b80601f01602080910402602001604051908101604052809291908181526020018280546119039061471f565b801561194e5780601f106119255761010080835404028352916020019161194e565b820191905f5260205f20905b81548152906001019060200180831161193157829003601f168201915b5050505050815260200160018201805480602002602001604051908101604052809291908181526020015f905b82821015611afb575f8481526020908190206040805160a08101825260048602909201805460ff8116845261010090046001600160f81b0316938301939093526001830154908201526002820180549192916060840191906119dc9061471f565b80601f0160208091040260200160405190810160405280929190818152602001828054611a089061471f565b8015611a535780601f10611a2a57610100808354040283529160200191611a53565b820191905f5260205f20905b815481529060010190602001808311611a3657829003601f168201915b50505050508152602001600382018054611a6c9061471f565b80601f0160208091040260200160405190810160405280929190818152602001828054611a989061471f565b8015611ae35780601f10611aba57610100808354040283529160200191611ae3565b820191905f5260205f20905b815481529060010190602001808311611ac657829003601f168201915b5050505050815250508152602001906001019061197b565b5050505081525050905090565b5f61083b6028613169565b6001611b1d612bf9565b6005811115611b2e57611b2e6140b8565b14611b3b57611679612bf9565b5f819003611b5c5760405163b931f4df60e01b815260040160405180910390fd5b5f611b6561044a565b90508015611c7b577f000000000000000000000000fc000000000000000000000000000000000000016001600160a01b03166323b872dd33611ba5613163565b6001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa158015611be0573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c0491906148e0565b6040516001600160e01b031960e085901b1681526001600160a01b03928316600482015291166024820152604481018490526064016020604051808303815f875af1158015611c55573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c7991906148fb565b505b5f8054630100000090046001600160e81b0316906003611c9a83614a01565b91906101000a8154816001600160e81b0302191690836001600160e81b0316021790555050611cc7611d38565b60065460405163603ab8b760e01b81526001600160a01b03929092169163603ab8b791611d0691879187916001600160801b0390911690600401614a54565b5f604051808303815f87803b158015611d1d575f5ffd5b505af1158015611d2f573d5f5f3e3d5ffd5b50505050505050565b5f611d416125c1565b6001600160a01b0316639b5e6a8b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d7c573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061083b91906148e0565b5f5f5f5f611dac611d38565b6001600160a01b0316336001600160a01b031614611ddd576040516327a891ff60e01b815260040160405180910390fd5b600b8888604051611def929190614a80565b9081526020016040518091039020545f03611e1d5760028054905f611e1383614a8f565b9190505550600190505b8415611e75578560015f828254611e349190614aa7565b9250508190555085600b8989604051611e4e929190614a80565b90815260200160405180910390205f828254611e6a9190614aa7565b90915550611f0c9050565b8560015f828254611e86919061492a565b9250508190555085600b8989604051611ea0929190614a80565b90815260200160405180910390205f828254611ebc919061492a565b9091555050604051600b90611ed4908a908a90614a80565b9081526020016040518091039020545f03611f0c5760028054905f611ef883614aba565b91905055508080611f0890614acf565b9150505b6001549350600b8888604051611f23929190614a80565b90815260200160405180910390205492506002549150945094509450949050565b5f831580611f50575081155b80611f5d57506009548414155b80611f6a57506008548214155b15611f8857604051630ae8117760e21b815260040160405180910390fd5b5f670de0b6b3a764000060608288888281611fa557611fa56146f4565b9050602002810190611fb79190614aec565b611fc5906020810190614b16565b6002811115611fd657611fd66140b8565b03611fff5750604080518082019091526007815266028a096a0a49d160cd1b602082015261208b565b6001888885818110612013576120136146f4565b90506020028101906120259190614aec565b612033906020810190614b16565b6002811115612044576120446140b8565b0361206c5750604080518082019091526006815265028a096a19d160d51b602082015261208b565b50604080518082019091526006815265028a096ab1d160d51b60208201525b86831015612273578787848181106120a5576120a56146f4565b90506020028101906120b79190614aec565b6120c8906040810190602001614b45565b6001600160581b03161561227357600a818989868181106120eb576120eb6146f4565b90506020028101906120fd9190614aec565b61210b906060810190614b60565b60405160200161211d93929190614ba2565b60408051601f1981840301815290829052612137916149f6565b9081526040519081900360200190205460ff166121ba5780888885818110612161576121616146f4565b90506020028101906121739190614aec565b612181906060810190614b60565b60405160200161219393929190614ba2565b60408051601f1981840301815290829052633cfbd52760e21b8252610ca491600401613fc3565b61226661225f612211600986815481106121d6576121d66146f4565b5f9182526020909120600290910201546122059061010090046001600160581b0316662386f26fc10000614bc0565b6001600160581b031690565b6122598b8b88818110612226576122266146f4565b90506020028101906122389190614aec565b612249906040810190602001614b45565b612205906509184e72a000614bc0565b906133f3565b8390613500565b600190930192915061208b565b828714612285575f93505050506125b9565b6040515f935061229a9060079060200161484f565b60405160208183030381529060405290505f6122b35f90565b90505b858410156124fa57600a828888878181106122d3576122d36146f4565b90506020028101906122e59190614be9565b6122f3906060810190614b60565b60405160200161230593929190614ba2565b60408051601f198184030181529082905261231f916149f6565b9081526040519081900360200190205460ff166123a25781878786818110612349576123496146f4565b905060200281019061235b9190614be9565b612369906060810190614b60565b60405160200161237b93929190614ba2565b60408051601f1981840301815290829052631f3f577f60e21b8252610ca491600401613fc3565b8686858181106123b4576123b46146f4565b90506020028101906123c69190614be9565b6123d7906040810190602001614bfd565b6001600160f81b0316600760010185815481106123f6576123f66146f4565b5f91825260209091206004909102015461010090046001600160f81b0316111561242157505f6124fa565b6124ed6124e66124a061245660076001018881548110612443576124436146f4565b905f5260205f2090600402016001015490565b61249a8b8b8a81811061246b5761246b6146f4565b905060200281019061247d9190614be9565b61248e906040810190602001614bfd565b6001600160f81b031690565b90613515565b6124e0600760010188815481106124b9576124b96146f4565b5f9182526020909120600490910201546124dd9060ff16662386f26fc10000614c18565b90565b90613500565b829061352c565b60019094019390506122b6565b83861461250d575f9450505050506125b9565b5f61251b6124dd8584613500565b90505f8160015461252c9190614aa7565b61253983620f4240614c18565b6125439190614c43565b6006549091505f90620f42409061256b908490600160801b90046001600160801b0316614c18565b6125759190614c43565b6006549091506001600160801b03161580159061259d57506006546001600160801b03168110155b156125b057506006546001600160801b03165b96505050505050505b949350505050565b5f61083b6014613169565b5f5f5f6125d7611d38565b6001600160a01b031663aa8ff04e3087876040518463ffffffff1660e01b815260040161260693929190614c62565b608060405180830381865afa158015612621573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906126459190614cbc565b935050509150816126695760405163b931f4df60e01b815260040160405180910390fd5b9150505b92915050565b61267b6125c1565b6001600160a01b0316336001600160a01b0316146126ac5760405163cf7527d960e01b815260040160405180910390fd5b604051600b60fb1b602082015260210160408051601f1981840301815291905280516020909101206126de8480614b60565b6040516020016126ef929190614a80565b6040516020818303038152906040528051906020012003612723576040516302448d4360e31b815260040160405180910390fd5b6004612730868883614d4f565b5083600561273e8282614e1f565b50839050600761274e828261507c565b9050505f60605b82821015612a14576009848484818110612771576127716146f4565b90506020028101906127839190614aec565b81546001810183555f92835260209092209091600202016127a48282615158565b505f90508484848181106127ba576127ba6146f4565b90506020028101906127cc9190614aec565b6127da906020810190614b16565b60028111156127eb576127eb6140b8565b036128145750604080518082019091526007815266028a096a0a49d160cd1b60208201526128a0565b6001848484818110612828576128286146f4565b905060200281019061283a9190614aec565b612848906020810190614b16565b6002811115612859576128596140b8565b036128815750604080518082019091526006815265028a096a19d160d51b60208201526128a0565b50604080518082019091526006815265028a096ab1d160d51b60208201525b600a818585858181106128b5576128b56146f4565b90506020028101906128c79190614aec565b6128d5906060810190614b60565b6040516020016128e793929190614ba2565b60408051601f1981840301815290829052612901916149f6565b9081526040519081900360200190205460ff1615612985578084848481811061292c5761292c6146f4565b905060200281019061293e9190614aec565b61294c906060810190614b60565b60405160200161295e93929190614ba2565b60408051601f1981840301815290829052634c368ddf60e11b8252610ca491600401613fc3565b6001600a8286868681811061299c5761299c6146f4565b90506020028101906129ae9190614aec565b6129bc906060810190614b60565b6040516020016129ce93929190614ba2565b60408051601f19818403018152908290526129e8916149f6565b908152604051908190036020019020805491151560ff1990921691909117905560019190910190612755565b5f9150612a218580614b60565b604051602001612a3292919061520b565b60405160208183030381529060405290505b612a516020860186614e87565b9050821015612bef57600a81612a6a6020880188614e87565b85818110612a7a57612a7a6146f4565b9050602002810190612a8c9190614be9565b612a9a906060810190614b60565b604051602001612aac93929190614ba2565b60408051601f1981840301815290829052612ac6916149f6565b9081526040519081900360200190205460ff1615612b555780612aec6020870187614e87565b84818110612afc57612afc6146f4565b9050602002810190612b0e9190614be9565b612b1c906060810190614b60565b604051602001612b2e93929190614ba2565b60408051601f19818403018152908290526307bd843d60e41b8252610ca491600401613fc3565b6001600a82612b676020890189614e87565b86818110612b7757612b776146f4565b9050602002810190612b899190614be9565b612b97906060810190614b60565b604051602001612ba993929190614ba2565b60408051601f1981840301815290829052612bc3916149f6565b908152604051908190036020019020805491151560ff1990921691909117905560019190910190612a44565b5050505050505050565b5f60045f5460ff166005811115612c1257612c126140b8565b1480612c33575060055f5460ff166005811115612c3157612c316140b8565b145b15612c4157505f5460ff1690565b612c496116f3565b4211612c5457505f90565b612c5c6116f3565b42118015612c715750612c6d610c31565b4211155b15612c92575f5462010000900460ff1615612c8c5750600290565b50600190565b50600390565b5f612ca360506133d2565b919050565b60045f5460ff166005811115612cc057612cc06140b8565b14612cde576040516306d27d2160e11b815260040160405180910390fd5b5f5f612ce8611d38565b6001600160a01b031663aa8ff04e3086866040518463ffffffff1660e01b8152600401612d1793929190614c62565b608060405180830381865afa158015612d32573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d569190614cbc565b93505050915081612d7a5760405163b931f4df60e01b815260040160405180910390fd5b805f03612d9a5760405163aebd15cf60e01b815260040160405180910390fd5b600b8484604051612dac929190614a80565b9081526020016040518091039020545f03612dda57604051630169d19960e31b815260040160405180910390fd5b5f600154600b8686604051612df0929190614a80565b908152602001604051809103902054620f4240612e0d9190614c18565b612e179190614c43565b6006549091505f90620f424090612e3f908490600160801b90046001600160801b0316614c18565b612e499190614c43565b6006549091505f906001600160801b03168103612e9257600254600103612e8d57600354600654612e8a9190600160801b90046001600160801b031661492a565b91505b612fe6565b6006546001600160801b03168210612eb3576006546001600160801b031691505b600254600114612f02575f54610100900460ff16158015612ede57506006546001600160801b031682105b15612efd57600654612efa9083906001600160801b031661492a565b90505b612f57565b600354600654612f229190600160801b90046001600160801b031661492a565b821015612f57576003546006548391612f4a91600160801b90046001600160801b031661492a565b612f54919061492a565b90505b8015612fe6576005546001600160a01b031663a9059cbb612f76610840565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602481018490526044016020604051808303815f875af1158015612fc0573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612fe491906148fb565b505b60055460405163a9059cbb60e01b81526001600160a01b038681166004830152602482018590529091169063a9059cbb906044016020604051808303815f875af1158015613036573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061305a91906148fb565b50600b878760405161306d929190614a80565b9081526040519081900360200190205f90819055600280549161308f83614aba565b9091555061309f90508183614aa7565b60035f8282546130af9190614aa7565b925050819055508082856001600160a01b03167f17bd1e02fad55c41d40ef34cebf779ffad5f6d531a597f3051e234e74c2c630e6003548b8b306040516130f99493929190615222565b60405180910390a46002545f03611d2f575f805460ff19166005179055600680546001600160801b0316905560405130815242907f4b458c68578d57188b55fc98cef8757c807194fb31378b88df039807cc5159029060200160405180910390a250505050505050565b5f61083b5f5b5f5f61317f600119368181013560f01c90030190565b929092013560601c92915050565b5f805460ff1916600417815560065481906001600160801b0316158015906131d657506006546002546001600160801b03600160801b83048116926131d492911690614c18565b105b156132f4576006546002546131f4916001600160801b031690614c18565b6006546132119190600160801b90046001600160801b031661492a565b6005549091506001600160a01b031663a9059cbb61322d610840565b6040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526001600160801b03841660248201526044016020604051808303815f875af115801561327f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132a391906148fb565b50600680548291906010906132c9908490600160801b90046001600160801b0316615254565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508091505090565b6006546001600160801b031615801561330d5750600254155b156133bf576005546001600160a01b031663a9059cbb61332b611b08565b60065460405160e084901b6001600160e01b03191681526001600160a01b039092166004830152600160801b90046001600160801b031660248201526044016020604051808303815f875af1158015613386573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906133aa91906148fb565b50600680546001600160801b03169055919050565b5f805461ff001916610100179055919050565b5f5f6133e8600119368181013560f01c90030190565b929092013592915050565b5f828281830361341c578015613409575f613413565b670de0b6b3a76400005b9250505061266d565b670de0b6b3a7640000820361343d57670de0b6b3a76400009250505061266d565b805f0361345657670de0b6b3a76400009250505061266d565b670de0b6b3a7640000810361346f57849250505061266d565b670de0b6b3a76400008211156134a05761349961349461348e8761353a565b86613500565b613661565b92506134f8565b5f6134bd6124dd846ec097ce7bc90715b34b9f1000000000614c43565b90505f6134d56134946134cf8461353a565b88613500565b90506134f36124dd826ec097ce7bc90715b34b9f1000000000614c43565b945050505b505092915050565b5f61350e6124dd84846136b5565b9392505050565b5f61350e6124dd84670de0b6b3a764000085613767565b5f61350e6124dd8385614aa7565b5f81670de0b6b3a76400008110156135685760405163036d32ef60e41b815260048101849052602401610ca4565b5f6135e9670de0b6b3a7640000830460016001600160801b03821160071b91821c6001600160401b03811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c60ff8111600390811b91821c600f811160021b90811c918211871b91821c969096119490961792909217171791909117919091171790565b9050670de0b6b3a7640000810282821c670de0b6b3a763ffff1981016136125750949350505050565b671bc16d674ec800006706f05b59d3b200005b801561365557670de0b6b3a764000083800204925081831061364d579283019260019290921c915b60011c613625565b50919695505050505050565b5f81680a688906bd8affffff8111156136905760405163b3b6ba1f60e01b815260048101849052602401610ca4565b5f6136a7670de0b6b3a7640000604084901b614c43565b90506125b96124dd82613835565b5f80805f19848609848602925082811083820303915050805f036136e65750670de0b6b3a76400009004905061266d565b670de0b6b3a7640000811061371857604051635173648d60e01b81526004810186905260248101859052604401610ca4565b5f670de0b6b3a764000085870962040000818503049310909103600160ee1b02919091177faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106690291505092915050565b5f80805f19858709858702925082811083820303915050805f0361379e5783828161379457613794614c2f565b049250505061350e565b8381106137cf57604051630c740aef60e31b8152600481018790526024810186905260448101859052606401610ca4565b5f84868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203025f889003889004909101858311909403939093029303949094049190911702949350505050565b600160bf1b67ff00000000000000821615613942576780000000000000008216156138695768016a09e667f3bcc9090260401c5b674000000000000000821615613888576801306fe0a31b7152df0260401c5b6720000000000000008216156138a7576801172b83c7d517adce0260401c5b6710000000000000008216156138c65768010b5586cf9890f62a0260401c5b6708000000000000008216156138e5576801059b0d31585743ae0260401c5b67040000000000000082161561390457680102c9a3e778060ee70260401c5b6702000000000000008216156139235768010163da9fb33356d80260401c5b67010000000000000082161561394257680100b1afa5abcbed610260401c5b66ff000000000000821615613a4157668000000000000082161561396f5768010058c86da1c09ea20260401c5b664000000000000082161561398d576801002c605e2e8cec500260401c5b66200000000000008216156139ab57680100162f3904051fa10260401c5b66100000000000008216156139c9576801000b175effdc76ba0260401c5b66080000000000008216156139e757680100058ba01fb9f96d0260401c5b6604000000000000821615613a055768010002c5cc37da94920260401c5b6602000000000000821615613a23576801000162e525ee05470260401c5b6601000000000000821615613a415768010000b17255775c040260401c5b65ff0000000000821615613b375765800000000000821615613a6c576801000058b91b5bc9ae0260401c5b65400000000000821615613a8957680100002c5c89d5ec6d0260401c5b65200000000000821615613aa65768010000162e43f4f8310260401c5b65100000000000821615613ac357680100000b1721bcfc9a0260401c5b65080000000000821615613ae05768010000058b90cf1e6e0260401c5b65040000000000821615613afd576801000002c5c863b73f0260401c5b65020000000000821615613b1a57680100000162e430e5a20260401c5b65010000000000821615613b37576801000000b1721835510260401c5b64ff00000000821615613c2457648000000000821615613b6057680100000058b90c0b490260401c5b644000000000821615613b7c5768010000002c5c8601cc0260401c5b642000000000821615613b98576801000000162e42fff00260401c5b641000000000821615613bb45768010000000b17217fbb0260401c5b640800000000821615613bd0576801000000058b90bfce0260401c5b640400000000821615613bec57680100000002c5c85fe30260401c5b640200000000821615613c085768010000000162e42ff10260401c5b640100000000821615613c2457680100000000b17217f80260401c5b63ff000000821615613d08576380000000821615613c4b5768010000000058b90bfc0260401c5b6340000000821615613c66576801000000002c5c85fe0260401c5b6320000000821615613c8157680100000000162e42ff0260401c5b6310000000821615613c9c576801000000000b17217f0260401c5b6308000000821615613cb757680100000000058b90c00260401c5b6304000000821615613cd25768010000000002c5c8600260401c5b6302000000821615613ced576801000000000162e4300260401c5b6301000000821615613d085768010000000000b172180260401c5b62ff0000821615613de35762800000821615613d2d576801000000000058b90c0260401c5b62400000821615613d4757680100000000002c5c860260401c5b62200000821615613d615768010000000000162e430260401c5b62100000821615613d7b57680100000000000b17210260401c5b62080000821615613d955768010000000000058b910260401c5b62040000821615613daf576801000000000002c5c80260401c5b62020000821615613dc957680100000000000162e40260401c5b62010000821615613de3576801000000000000b1720260401c5b61ff00821615613eb557618000821615613e0657680100000000000058b90260401c5b614000821615613e1f5768010000000000002c5d0260401c5b612000821615613e38576801000000000000162e0260401c5b611000821615613e515768010000000000000b170260401c5b610800821615613e6a576801000000000000058c0260401c5b610400821615613e8357680100000000000002c60260401c5b610200821615613e9c57680100000000000001630260401c5b610100821615613eb557680100000000000000b10260401c5b60ff821615613f7e576080821615613ed657680100000000000000590260401c5b6040821615613eee576801000000000000002c0260401c5b6020821615613f0657680100000000000000160260401c5b6010821615613f1e576801000000000000000b0260401c5b6008821615613f3657680100000000000000060260401c5b6004821615613f4e57680100000000000000030260401c5b6002821615613f6657680100000000000000010260401c5b6001821615613f7e57680100000000000000010260401c5b670de0b6b3a76400000260409190911c60bf031c90565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f61350e6020830184613f95565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b038111828210171561401157614011613fd5565b604052919050565b5f6001600160401b0382111561403157614031613fd5565b50601f01601f191660200190565b5f6020828403121561404f575f5ffd5b81356001600160401b03811115614064575f5ffd5b8201601f81018413614074575f5ffd5b803561408761408282614019565b613fe9565b81815285602083850101111561409b575f5ffd5b816020840160208301375f91810160200191909152949350505050565b634e487b7160e01b5f52602160045260245ffd5b5f82825180855260208501945060208160051b830101602085015f5b8381101561416757848303601f19018852815180516003811061410d5761410d6140b8565b84526020818101516001600160581b0316908501526040808201516001600160a01b03169085015260609081015160809185018290529061415090850182613f95565b6020998a01999094509290920191506001016140e8565b50909695505050505050565b602081525f61350e60208301846140cc565b80516001600160a01b031682526020808201516001600160801b039081169184019190915260409182015116910152565b6060810161266d8284614185565b5f602082840312156141d4575f5ffd5b5035919050565b5f8151606084526141ef6060850182613f95565b9050602083015184820360208601526142088282613f95565b915050604083015160408501528091505092915050565b5f8151604084526142336040850182613f95565b60208481015186830387830152805180845292935081019181840191600582901b8501015f5b828110156142da57601f19868303018452845160ff815116835260018060f81b03602082015116602084015260408101516040840152606081015160a060608501526142a860a0850182613f95565b90506080820151915083810360808501526142c38183613f95565b602097880197969096019593505050600101614259565b50979650505050505050565b600681106142f6576142f66140b8565b9052565b61022081525f895160a06102208401526143186102c0840182613f95565b905060208b015161021f19848303016102408501526143378282613f95565b60408d01516001600160a01b031661026086015260608d015161028086015260808d01516102a0860152915061439e9050602084018b80516001600160401b031682526020808201516001600160a01b039081169184019190915260409182015116910152565b6143ab608084018a614185565b82810360e08401526143bd81896141db565b90508281036101008401526143d2818861421f565b90508281036101208401526143e781876140cc565b85516101408501526020860151610160850152604086015161018085015260608601516101a085015260808601516101c085015260a08601516101e0850152915061442f9050565b61443d6102008301846142e6565b9998505050505050505050565b6001600160a01b038116811461445e575f5ffd5b50565b5f5f60408385031215614472575f5ffd5b823561447d8161444a565b946020939093013593505050565b602081525f61350e602083018461421f565b5f5f83601f8401126144ad575f5ffd5b5081356001600160401b038111156144c3575f5ffd5b6020830191508360208285010111156144da575f5ffd5b9250929050565b5f5f602083850312156144f2575f5ffd5b82356001600160401b03811115614507575f5ffd5b6145138582860161449d565b90969095509350505050565b801515811461445e575f5ffd5b5f5f5f5f6060858703121561453f575f5ffd5b84356001600160401b03811115614554575f5ffd5b6145608782880161449d565b90955093505060208501359150604085013561457b8161451f565b939692955090935050565b5f5f83601f840112614596575f5ffd5b5081356001600160401b038111156145ac575f5ffd5b6020830191508360208260051b85010111156144da575f5ffd5b5f5f5f5f604085870312156145d9575f5ffd5b84356001600160401b038111156145ee575f5ffd5b6145fa87828801614586565b90955093505060208501356001600160401b03811115614618575f5ffd5b61462487828801614586565b95989497509550505050565b5f5f5f5f5f5f86880360c0811215614646575f5ffd5b87356001600160401b0381111561465b575f5ffd5b6146678a828b0161449d565b9098509650506060601f198201121561467e575f5ffd5b5060208701935060808701356001600160401b0381111561469d575f5ffd5b87016040818a0312156146ae575f5ffd5b925060a08701356001600160401b038111156146c8575f5ffd5b6146d489828a01614586565b979a9699509497509295939492505050565b6020810161266d82846142e6565b634e487b7160e01b5f52603260045260245ffd5b5f81518060208401855e5f93019283525090919050565b600181811c9082168061473357607f821691505b60208210810361475157634e487b7160e01b5f52602260045260245ffd5b50919050565b5f81546147638161471f565b60018216801561477a576001811461478f576147bc565b60ff19831686528115158202860193506147bc565b845f5260205f205f5b838110156147b457815488820152600190910190602001614798565b505081860193505b50505092915050565b5f6125b96147d38386614708565b84614757565b60ff8116811461445e575f5ffd5b6001600160f81b038116811461445e575f5ffd5b5f5f5f5f6080858703121561480e575f5ffd5b84516004811061481c575f5ffd5b602086015190945061482d816147d9565b604086015190935061483e816147e7565b606086015190925061457b8161444a565b5f61485a8284614757565b6101d160f51b81526002019392505050565b5f6020828403121561487c575f5ffd5b81516001600160401b03811115614891575f5ffd5b8201601f810184136148a1575f5ffd5b80516148af61408282614019565b8181528560208385010111156148c3575f5ffd5b8160208401602083015e5f91810160200191909152949350505050565b5f602082840312156148f0575f5ffd5b815161350e8161444a565b5f6020828403121561490b575f5ffd5b815161350e8161451f565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561266d5761266d614916565b5f606082840312801561494e575f5ffd5b50604051606081016001600160401b038111828210171561497157614971613fd5565b60405282516001600160401b038116811461498a575f5ffd5b8152602083015161499a8161444a565b602082015260408301516149ad8161444a565b60408201529392505050565b5f602082840312156149c9575f5ffd5b815161350e816147d9565b5f5f604083850312156149e5575f5ffd5b505080516020909101519092909150565b5f61350e8284614708565b5f6001600160e81b0382166002600160e81b03198101614a2357614a23614916565b60010192915050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b604081525f614a67604083018587614a2c565b90506001600160801b0383166020830152949350505050565b818382375f9101908152919050565b5f60018201614aa057614aa0614916565b5060010190565b8082018082111561266d5761266d614916565b5f81614ac857614ac8614916565b505f190190565b5f81810b60808101614ae357614ae3614916565b5f190192915050565b5f8235607e19833603018112614b00575f5ffd5b9190910192915050565b6003811061445e575f5ffd5b5f60208284031215614b26575f5ffd5b813561350e81614b0a565b6001600160581b038116811461445e575f5ffd5b5f60208284031215614b55575f5ffd5b813561350e81614b31565b5f5f8335601e19843603018112614b75575f5ffd5b8301803591506001600160401b03821115614b8e575f5ffd5b6020019150368190038213156144da575f5ffd5b5f614bad8286614708565b838582375f930192835250909392505050565b6001600160581b038181168382160290811690818114614be257614be2614916565b5092915050565b5f8235609e19833603018112614b00575f5ffd5b5f60208284031215614c0d575f5ffd5b813561350e816147e7565b808202811582820484141761266d5761266d614916565b634e487b7160e01b5f52601260045260245ffd5b5f82614c5d57634e487b7160e01b5f52601260045260245ffd5b500490565b6001600160a01b03841681526060602082018190525f90614c869083018486614a2c565b828103604093840152601481527310d3d3919251ce88105d5d1a1bdc95d85b1b195d60621b602082015291909101949350505050565b5f5f5f5f60808587031215614ccf575f5ffd5b8451614cda8161451f565b60208601516040870151606090970151919890975090945092505050565b5b81811015614d0c575f8155600101614cf9565b5050565b601f821115614d4a57805f5260205f20601f840160051c81016020851015614d355750805b614d47601f850160051c830182614cf8565b50505b505050565b6001600160401b03831115614d6657614d66613fd5565b614d7a83614d74835461471f565b83614d10565b5f601f841160018114614dab575f8515614d945750838201355b5f19600387901b1c1916600186901b178355614d47565b5f83815260208120601f198716915b82811015614dda5786850135825560209485019460019092019101614dba565b5086821015614df6575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b5f81356001600160801b038116811461266d575f5ffd5b8135614e2a8161444a565b81546001600160a01b0319166001600160a01b03919091161781556001600160801b03614e5960208401614e08565b166fffffffffffffffffffffffffffffffff19614e7860408501614e08565b60801b16176001919091015550565b5f5f8335601e19843603018112614e9c575f5ffd5b8301803591506001600160401b03821115614eb5575f5ffd5b6020019150600581901b36038213156144da575f5ffd5b8054614ed79061471f565b8015614d0c57601f811160018114614ef05750505f9055565b5f83815260209020614f0d601f840160051c820160018301614cf8565b505f8381526020812081855555505050565b8135614f2a816147d9565b60ff811690508060ff198354161782556020830135614f48816147e7565b60081b60ff191617815560408201356001820155614f696060830183614b60565b614f77818360028601614d4f565b5050614f866080830183614b60565b6118a1818360038601614d4f565b68010000000000000000831115614fad57614fad613fd5565b805483825580841015615037576001600160fe1b0381168114614fd257614fd2614916565b6001600160fe1b0384168414614fea57614fea614916565b815f5260205f208160021b81018560021b820191505b80821015615034575f82555f600183015561501d60028301614ecc565b61502960038301614ecc565b600482019150615000565b50505b505f8181526020812083915b858110156150745761505e6150588487614be9565b83614f1f565b6020929092019160049190910190600101615043565b505050505050565b6150868283614b60565b6001600160401b0381111561509d5761509d613fd5565b6150b1816150ab855461471f565b85614d10565b5f601f8211600181146150e2575f83156150cb5750838201355b5f19600385901b1c1916600184901b178555615139565b5f85815260208120601f198516915b8281101561511157868501358255602094850194600190920191016150f1565b508482101561512d575f1960f88660031b161c19848701351681555b505060018360011b0185555b5050505061514a6020830183614e87565b6118a1818360018601614f94565b813561516381614b0a565b60038110615173576151736140b8565b815460ff821691508160ff198216178355602084013561519281614b31565b6bffffffffffffffffffffff008160081b16836bffffffffffffffffffffffff1984161717845550505060408201356151ca8161444a565b81546bffffffffffffffffffffffff16606091821b6bffffffffffffffffffffffff19161782556151fd90830183614b60565b6118a1818360018601614d4f565b818382376101d160f51b9101908152600201919050565b848152606060208201525f61523b606083018587614a2c565b905060018060a01b038316604083015295945050505050565b6001600160801b03828116828216039081111561266d5761266d61491656fea2646970667358221220bde31421032b967b56a78e8b89516ec194451095a77a79c0e8ebe5c6cf47211064736f6c634300081e0033
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
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.