Source Code
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Cross-Chain Transactions
Loading...
Loading
Contract Name:
EchoCampaign
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 200 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
// ███████╗ ██████╗██╗ ██╗ ██████╗ ███╗ ███╗ █████╗ ██████╗ ██╗ ██╗███████╗████████╗
// ██╔════╝██╔════╝██║ ██║██╔═══██╗ ████╗ ████║██╔══██╗██╔══██╗██║ ██╔╝██╔════╝╚══██╔══╝
// █████╗ ██║ ███████║██║ ██║ ██╔████╔██║███████║██████╔╝█████╔╝ █████╗ ██║
// ██╔══╝ ██║ ██╔══██║██║ ██║ ██║╚██╔╝██║██╔══██║██╔══██╗██╔═██╗ ██╔══╝ ██║
// ███████╗╚██████╗██║ ██║╚██████╔╝ ██║ ╚═╝ ██║██║ ██║██║ ██║██║ ██╗███████╗ ██║
// ╚══════╝ ╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚═╝
// ================================= EchoCampaign V1 =================================
// =================================== Spring 2025 ===================================
import {IERC20} from "./interfaces/IERC20.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 EchoLab and Dynabits.org
contract EchoCampaign is IEchoCampaign, Clone {
/********************************\
|-*-*-*-*-* STATES *-*-*-*-*-|
\********************************/
Status private _status;
bool private _campaignPaused;
uint240 private _totalPosts;
uint256 private _totalEffectiveKPIs;
uint256 private _totalEligibleContents;
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 constant FRAX_USD =
// FOR PRODUCTION
// 0xFc00000000000000000000000000000000000001;
// FOR TESTS
0xa35Bd0843e49F4eCF7Fb716FDADdc86f4482dB8E;
/********************************\
|-*-*-*-*-* 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,
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 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 *-*-*-*-|
\******************************/
/// @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();
// Store campaign metadata.
_ipfsCID = ipfsCID;
_budgetInfo = budgetInfo_;
_socialKPIs = socialKPIs_;
uint256 i;
string memory qaAction;
// Register Qualification Actions.
while (i < qaData_.length) {
_qaData.push(qaData_[i]);
// Determine the QA method prefix.
if (qaData_[i].method == QA_METHOD.AI) qaAction = "QA-AI: ";
else if (qaData_[i].method == QA_METHOD.COMMUNITY)
qaAction = "QA-C: ";
else qaAction = "QA-V: ";
// Revert the operation if a duplicate qualification is detected.
if (registeredActionKind[string.concat(qaAction, qaData_[i].kind)])
revert DUPLICATE_QA(string.concat(qaAction, qaData_[i].kind));
// Register the Qualification.
registeredActionKind[
string.concat(qaAction, qaData_[i].kind)
] = true;
unchecked {
i++;
}
}
// Register Social KPI Actions.
delete i;
while (i < socialKPIs_.kpis.length) {
// Revert the operation if a duplicate KPI is detected.
if (
registeredActionKind[
string.concat(
socialKPIs_.social,
": ",
socialKPIs_.kpis[i].method
)
]
)
revert DUPLICATE_KPI(
string.concat(
socialKPIs_.social,
": ",
socialKPIs_.kpis[i].method
)
);
// Register the SocialKPI.
registeredActionKind[
string.concat(
socialKPIs_.social,
": ",
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)
);
}
/*******************************\
|*-*-*-* 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.
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);
}
/// @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
)
{
// If this is the first KPI update for the content, increase the eligible content count.
if (contentEffectiveKPI[contentLink] == 0) _totalEligibleContents++;
// 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--;
}
// 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: Author");
// 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] * 1e5) /
_totalEffectiveKPIs;
uint256 rewardAmount = (_budgetInfo.reservedAmount * contentKPIratio) /
1e5;
uint256 refundAmount;
// Adjust reserved budget and determine refund amount if applicable.
if (_budgetInfo.maxPerPost == 0) {
if (_totalEligibleContents != 1)
_budgetInfo.reservedAmount -= uint128(rewardAmount);
else rewardAmount = _budgetInfo.reservedAmount;
} else {
if (rewardAmount >= _budgetInfo.maxPerPost)
rewardAmount = _budgetInfo.maxPerPost;
if (_totalEligibleContents != 1) {
if (rewardAmount < _budgetInfo.maxPerPost)
refundAmount = _budgetInfo.maxPerPost - rewardAmount;
_budgetInfo.reservedAmount -= _budgetInfo.maxPerPost;
} else {
if (rewardAmount < _budgetInfo.reservedAmount)
refundAmount = _budgetInfo.reservedAmount - rewardAmount;
}
// Refund excess funds if applicable.
if (refundAmount != 0) {
IERC20(_budgetInfo.token).transferFrom(
address(this),
refundAddress(),
refundAmount
);
}
}
// Transfer the calculated reward to the content's registered author.
IERC20(_budgetInfo.token).transferFrom(
address(this),
address(uint160(actionKindRes)),
rewardAmount
);
// Remove the content's KPI score tracking and decrement the eligible content counter.
delete contentEffectiveKPI[contentLink];
_totalEligibleContents--;
// Emit an event to log the claim transaction.
emit Claimed(
address(uint160(actionKindRes)),
rewardAmount,
refundAmount,
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++;
}
}
// 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 * 1e5) /
(_totalEffectiveKPIs + effectiveKPI);
// Determine the reward amount based on the reserved campaign budget.
uint256 rewardAmount = (_budgetInfo.reservedAmount * contentKPIratio) /
1e5;
// 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(
IERC20(_budgetInfo.token).name(),
IERC20(_budgetInfo.token).symbol(),
IERC20(_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 currentStatus() public view returns (Status) {
// Check if the current timestamp is before the campaign start time.
if (block.timestamp <= startTime()) return Status.upcoming;
// Check if the campaign is within its active duration and not in a finalized state.
if (
block.timestamp > startTime() &&
block.timestamp <= endTime() &&
_status != Status.claimable &&
_status != Status.finalized
)
if (_campaignPaused) return Status.paused;
else return Status.inProgress;
// If the current timestamp exceeds the campaign end time, mark it as finished.
else if (block.timestamp > endTime()) return Status.finished;
// Default return the stored campaign status.
return _status;
}
/// @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))
);
unchecked {
// Accumulate the fee.
totalFee += tempFee;
i++;
}
}
// Reset iteration variables for the next calculation.
delete i;
delete totalFee;
length = _socialKPIs.kpis.length;
action = _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)
)
);
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 refundAmount)
{
_status = Status.claimable;
if (
_budgetInfo.maxPerPost != 0 &&
_totalEligibleContents * _budgetInfo.maxPerPost <
_budgetInfo.reservedAmount
) {
refundAmount = uint128(
_totalEligibleContents * _budgetInfo.maxPerPost
);
IERC20(_budgetInfo.token).transferFrom(
address(this),
refundAddress(),
_budgetInfo.reservedAmount - refundAmount
);
_budgetInfo.reservedAmount -= refundAmount;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
/// @notice ERC20 interface, which is used for easier interactions with ERC20 contracts.
interface IERC20 {
/**
@notice Allows the contract to transfer Deft tokens to a specified address.
@dev Allows the contract to transfer Deft tokens to a specified address.
@param to The address to which the Deft tokens will be transferred.
@param amount The amount of Deft tokens to be transferred.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
@notice Allows the contract to transfer Deft tokens from one address to another.
@dev Allows the contract to transfer Deft tokens from one address to another.
@param from The address from which the Deft tokens will be transferred.
@param to The address to which the Deft tokens will be transferred.
@param amount The amount of Deft tokens to be transferred.
@return bool indicating if the transfer was successful or not.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function name() external view returns(string memory);
function symbol() external view returns(string memory);
function decimals() external view returns(uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
/***************************\
|*-*-*-* TYPES *-*-*-*|
\***************************/
enum ActionStatus {
NA,
Config,
QA,
KPI
}
struct ActionAndKind {
ActionStatus actionStatus;
string action;
string kind;
}
struct ModelData {
ActionStatus actionStatus;
uint8 socialKPIratio;
uint248 fee;
address oracle;
}
/// @title Echo Administration Interface
/// @notice Defines administrative functions for managing campaign settings, token whitelisting, and oracle permissions.
/// @author Dynabits.org
interface IEchoAdministration {
/*******************************\
|*-*-*-* EXTERNALS *-*-*-*-*|
\*******************************/
/// @notice Transfers protocol admin rights to a new address.
/// @dev Only the current protocol admin can call this function. The new admin address cannot be the zero address.
/// @param newProtocolAdmin The address of the new protocol admin.
function changeProtocolAdmin(address newProtocolAdmin) external;
/// @notice Sets the campaign fee rate as a percentage of the allocated token at creation.
/// @dev The value must be between 0 and 1e5.
/// @param newCampaignFeeRate The new campaign creation fee rate.
function changeCampaignFeeRate(uint24 newCampaignFeeRate) external;
/// @notice Sets the maximum campaign duration.
/// @dev A value of zero means there is no maximum campaign duration.
/// @param newMaxCampaignTime The maximum campaign duration in seconds.
function setMaxCampaignTime(uint72 newMaxCampaignTime) external;
/// @notice Whitelists a new ERC20 token for use in campaigns.
/// @dev A token cannot be whitelisted more than once. The whitelist status is checked during campaign creation.
/// @param token The ERC20 token address.
function whitelistToken(address token) external;
/// @notice Removes an ERC20 token from the whitelist, preventing its selection as a campaign budget token.
/// @param token The ERC20 token address.
function removeWhitelistedToken(address token) external;
/// @notice Toggles full access permissions for an oracle.
/// @dev This function can be used to temporarily or permanently disable an oracle’s access to campaign-related functionalities.
/// @param oracle The address of the oracle.
function toggleOracleFullAccess(address oracle) external;
/**
* @notice Sets or updates the model data for a given action and kind.
* @dev Configures an action-kind mapping with an associated oracle, fee, and status.
* @param actionAndKind The struct containing the action and kind identifiers.
* @param model The struct containing model details such as the oracle, fee, status, and social KPI ratio.
*
* Requirements:
* - `actionAndKind.action` and `actionAndKind.kind` must not be empty.
* - `model.actionStatus` must be one of `QA`, `KPI`, or `CONFIG`.
* - Actions must match their respective `actionStatus`:
* - `QA` actions: `QA-AI`, `QA-C`, `QA-V`
* - `KPI` actions: `KPI`
* - `CONFIG` actions: `CONFIG`
* - QA oracles must have a nonzero fee.
* - KPI oracles must have both a nonzero fee and a nonzero social KPI ratio.
* - CONFIG actions always have a zero fee.
*
* Effects:
* - Adds or updates an action-kind mapping.
* - Removes model data if `model.oracle` is zero.
*
* Emits:
* - `ModelSettled` if a model is set or updated.
* - `ModelRemoved` if a model is removed.
*
* Reverts:
* - `EMPTY_ACTION_OR_KIND_PROVIDED` if either action or kind is empty.
* - `NA_ACTION_STATUS` if the provided action status is `NA`.
* - `NOT_RELATIVE_ACTION` if the action does not match its required type.
* - `UNDEFINED_PROCESS` if a QA oracle has a fee but no valid address.
* - `ZERO_FEE_PROVIDED` if a required fee is zero.
* - `ZERO_SOCIAL_KPI_PROVIDED` if a required social KPI ratio is zero.
*/
function setModel(
ActionAndKind calldata actionAndKind,
ModelData memory model
) external;
/*****************************\
|-*-*-*-* GETTERS *-*-*-*-|
\*****************************/
/// @notice Returns the campaign creation fee rate as a percentage.
/// @dev This value is deducted from the allocated budget immediately upon campaign creation.
/// @return The campaign creation fee rate.
function campaignFeeRate() external view returns (uint24);
/// @notice Returns the maximum allowed duration for campaigns.
/// @dev A campaign’s duration (end time - start time) must be less than or equal to this value at creation.
/// @return The maximum campaign duration in seconds.
function maxCampaignTime() external view returns (uint72);
/// @notice Returns the protocol admin address.
/// @dev The protocol admin has full access to infrastructural modifications.
/// @return The address of the protocol admin.
function protocolAdmin() external view returns (address);
/// @notice Checks if a given ERC20 token is whitelisted for campaign use.
/// @param token The ERC20 token address.
/// @return A boolean indicating whether the token is whitelisted.
function whitelistedToken(address token) external view returns (bool);
/**
* @notice Checks if an oracle has been banned.
* @dev A banned oracle loses all permissions to interact with EchoContents and campaign contracts.
* @param oracle The address of the oracle.
* @return A boolean indicating whether the oracle is banned.
*/
function disallowedOracle(address oracle) external view returns (bool);
/**
* @notice Returns the number of available methods and parameters for a given action.
* @dev Examples:
* - KPI actions (e.g., `X`) can have parameters like `Like` and `Retweet`.
* - Qualification actions (`QA-V`) can have parameters like `Accuracy` and `Originality`.
* @param action The action type, such as `X`, `QA-V`, or `Config`.
* @return The number of available methods for the action.
*/
function totalKindMethods(string calldata action)
external
view
returns (uint256);
/**
* @notice Retrieves the original action and kind from a given hashed identifier.
* @dev Useful for reducing gas costs and simplifying comparisons.
* @param hashedActionKind The hash of the concatenated action and kind.
* @return The decoded action and kind as a string.
*/
function hashToActionKind(bytes32 hashedActionKind)
external
view
returns (string memory);
/**
* @notice Returns model data associated with an oracle and a hashed action-kind.
* @param oracle_ The address of the oracle.
* @param hashedActionKind The hashed action-kind identifier.
* @return actionStatus The action status of the model.
* @return socialKPIratio The social KPI ratio.
* @return fee The associated fee for the action.
* @return oracle The address of the oracle managing the action.
*/
function modelData(address oracle_, bytes32 hashedActionKind)
external
view
returns (
ActionStatus actionStatus,
uint8 socialKPIratio,
uint248 fee,
address oracle
);
/**
* @notice Checks if an oracle has the required permissions for given action-kind hashes.
* @dev Access is determined based on the oracle's address:
* - `address(0)`: Checks if the hashed action-kind exists for KPI.
* - `address(1)`: Checks if the hashed action-kind exists for CONFIG.
* - Any other address: Checks if the oracle has QA access.
* @param oracle The oracle’s address.
* @param hashedActionsKind An array of hashed action-kind values to verify.
* @return A boolean indicating whether the oracle is authorized.
*/
function allowedActionKind(
address oracle,
bytes32[] calldata hashedActionsKind
) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import {CampaignInitData, QualificationData, BudgetInfo, SocialKPIs} 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.20;
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 ContentDetails {
bool contentRegistered;
uint8 totalQaKinds;
uint240 lastUpdatedTime;
UD60x18 contentOverallQaScore;
mapping(string => uint256) actionKindToValue;
}
struct CampaignContentInfo {
uint128 lastUpdatedTime;
uint128 lastPostTime;
string[] contents;
}
struct CampaignAndContent {
address campaign;
string content;
}
/**
* @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.
*
* 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) 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.20;
// ["1745925656", "1747925656", "0x62F59422c5F76eee92A0425E6aA54426E43ADEAD", "bafkreiahcbhenoyrgjw4ndvuxyjj65ii47suar5i2yk7sad3umkyklunp4", ["0xa35Bd0843e49F4eCF7Fb716FDADdc86f4482dB8E", "100000000000000000000", "10000000000000000000000"], ["X", [["100", "10", "0", "Like", ""]]], [["0", "100", "0x62F59422c5F76eee92A0425E6aA54426E43ADEAD", "Accuracy And Credibility"]]]
/***************************\
|*-*-*-* TYPES *-*-*-*|
\***************************/
enum QA_METHOD {
AI,
COMMUNITY,
VOTE
}
enum Status {
upcoming,
inProgress,
paused,
finished,
claimable,
finalized
}
enum Finalizer {
protocolAdmin,
finalizerOracle,
contentCreator
}
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 KPI {
uint8 pct;
uint248 min;
uint256 ratio;
string method;
string extra;
}
struct SocialKPIs {
string social;
KPI[] kpis;
}
struct QualificationData {
QA_METHOD method;
uint88 pct;
address oracle;
string kind;
}
struct PostData {
string postLink;
address applicant;
}
/**
* @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.
*
* Requirements:
* - Caller must be the EchoContetns contract.
*/
function updateKPIs(
string calldata contentLink,
uint256 additionalContentEffectiveKPI,
bool additional
)
external
returns (
uint256 totalEffectiveKPIs,
uint256 contentEffectiveKPI_,
uint256 totalEligibleContents
);
/**
* @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 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: BSD
pragma solidity 0.8.20;
/// @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.19; /* ██████╗ ██████╗ ██████╗ ███╗ ███╗ █████╗ ████████╗██╗ ██╗ ██╔══██╗██╔══██╗██╔══██╗████╗ ████║██╔══██╗╚══██╔══╝██║ ██║ ██████╔╝██████╔╝██████╔╝██╔████╔██║███████║ ██║ ███████║ ██╔═══╝ ██╔══██╗██╔══██╗██║╚██╔╝██║██╔══██║ ██║ ██╔══██║ ██║ ██║ ██║██████╔╝██║ ╚═╝ ██║██║ ██║ ██║ ██║ ██║ ╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ██╗ ██╗██████╗ ██████╗ ██████╗ ██╗ ██╗ ██╗ █████╗ ██║ ██║██╔══██╗██╔════╝ ██╔═████╗╚██╗██╔╝███║██╔══██╗ ██║ ██║██║ ██║███████╗ ██║██╔██║ ╚███╔╝ ╚██║╚█████╔╝ ██║ ██║██║ ██║██╔═══██╗████╔╝██║ ██╔██╗ ██║██╔══██╗ ╚██████╔╝██████╔╝╚██████╔╝╚██████╔╝██╔╝ ██╗ ██║╚█████╔╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚════╝ */ import "./ud60x18/Casting.sol"; import "./ud60x18/Constants.sol"; import "./ud60x18/Conversions.sol"; import "./ud60x18/Errors.sol"; import "./ud60x18/Helpers.sol"; import "./ud60x18/Math.sol"; import "./ud60x18/ValueType.sol";
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.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;
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 { 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 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 { 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 "../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 "./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;
// 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 { 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 "./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 { 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;
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 { 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;
/// @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 "../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 "./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 { 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 "../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 "../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 { 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);// 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 { 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);{
"remappings": [
"ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@prb/math/=lib/prb-math/",
"prb-math/=lib/prb-math/src/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "shanghai",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"CAMPAIGN_PAUSED","type":"error"},{"inputs":[],"name":"CHECK_QAS_OR_KPIS","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":"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":"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":[],"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"}],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
608060405234801561000f575f80fd5b50614ea38061001d5f395ff3fe608060405234801561000f575f80fd5b5060043610610187575f3560e01c80638c7a8eb3116100d9578063b7701e4411610093578063ef8a92351161006e578063ef8a9235146103b1578063f172a4ce146103c6578063f3fe12c9146103ce578063f851a440146103e1575f80fd5b8063b7701e4414610383578063c45a015514610396578063e80099481461039e575f80fd5b80638c7a8eb3146102df5780638da5cb5b146102f4578063951f8239146102fc578063955c98671461030f578063a7c64c7014610317578063aa588b4b14610345575f80fd5b806326f016371161014457806349c26fcb1161011f57806349c26fcb146102a0578063565974d3146102b357806373e4fc0b146102cf57806378e97925146102d7575f80fd5b806326f01637146102185780633197cbb61461022d5780633326ee9c14610235575f80fd5b806304cbb47f1461018b57806306fdde03146101a65780630cb61f6c146101bb5780630ebf14b5146101db5780631f55479f146101e557806324034a0e146101ed575b5f80fd5b6101936103e9565b6040519081526020015b60405180910390f35b6101ae61079b565b60405161019d9190613bde565b6101c361081f565b6040516001600160a01b03909116815260200161019d565b6101e361082a565b005b6101e3610920565b6101936101fb366004613c5a565b8051602081830181018051600a8252928201919093012091525481565b610220610abe565b60405161019d9190613d88565b610193610c0e565b610293604080516060810182525f808252602082018190529181019190915250604080516060810182526004546001600160a01b031681526005546001600160801b038082166020840152600160801b909104169181019190915290565b60405161019d9190613dcb565b6101e36102ae366004613dd9565b610c19565b6102bb610d6e565b60405161019d989796959493929190613f14565b6101e3611590565b6101936116ca565b6102e76116d5565b60405161019d9190614064565b6101c3611936565b6101e361030a3660046140ba565b611941565b6101c3611b21565b61032a610325366004614108565b611b89565b6040805193845260208401929092529082015260600161019d565b610373610353366004613c5a565b805160208183018101805160098252928201919093012091525460ff1681565b604051901515815260200161019d565b6101936103913660046141a2565b611d19565b6101c3612383565b6101e36103ac366004614208565b61238e565b6103b961288f565b60405161019d91906142ba565b610193612942565b6101e36103dc3660046140ba565b612952565b6101c3612e38565b6008545f9081808060605b848410156105fe575f60088581548110610410576104106142c8565b5f91825260209091206002918202015460ff169081111561043357610433613cd3565b0361045c5750604080518082019091526007815266028a096a0a49d160cd1b60208201526104db565b600160088581548110610471576104716142c8565b5f91825260209091206002918202015460ff169081111561049457610494613cd3565b036104bc5750604080518082019091526006815265028a096a19d160d51b60208201526104db565b50604080518082019091526006815265028a096ab1d160d51b60208201525b6104e3612e38565b6001600160a01b031663b6001e2460088681548110610504576105046142c8565b905f5260205f2090600202015f01600c9054906101000a90046001600160a01b0316836008888154811061053a5761053a6142c8565b905f5260205f20906002020160010160405160200161055a929190614383565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b81526004016105a19291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa1580156105bc573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105e091906143df565b506001909601956001600160f81b03169485019493506103f4915050565b600754600680549196505f955085945090610618906142dc565b80601f0160208091040260200160405190810160405280929190818152602001828054610644906142dc565b801561068f5780601f106106665761010080835404028352916020019161068f565b820191905f5260205f20905b81548152906001019060200180831161067257829003601f168201915b505050505090505b84841015610791576106a7612e38565b6001600160a01b031663b6001e245f83600660010188815481106106cd576106cd6142c8565b905f5260205f2090600402016002016040516020016106ed929190614433565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b81526004016107349291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa15801561074f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061077391906143df565b506001909601956001600160f81b0316948501949350610697915050565b5090949350505050565b60606107a5612383565b6001600160a01b031663fe4d55366107bb612942565b6040518263ffffffff1660e01b81526004016107d991815260200190565b5f60405180830381865afa1580156107f3573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261081a919081019061445d565b905090565b5f61081a603c612e3e565b610832612e38565b6001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa15801561086d573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061089191906144c5565b6001600160a01b0316336001600160a01b0316146108c2576040516315cead3960e31b815260040160405180910390fd5b5f805460ff610100808304821615810261ff00199093169290921792839055604051308152919092049091161515907f771cd98ea23fa6204e979f96eecb381b08abb79f1a85d2a4e7b840086feeb86f9060200160405180910390a2565b5f610929612e38565b6001600160a01b031663b6001e246001604051602001610960907021a7a72324a39d102334b730b634bd32b960791b815260110190565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b81526004016109a79291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa1580156109c2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109e691906143df565b9350505050806001600160a01b0316336001600160a01b031614610a1d576040516389dd324f60e01b815260040160405180910390fd5b6003610a2761288f565b6005811115610a3857610a38613cd3565b14610a56576040516380bb6a1760e01b815260040160405180910390fd5b5f610a5f612e62565b6001600160801b0316905080600160055460408051600160801b9092046001600160801b0316825230602083015233917f4bf986268e3c9b6b83f7e5fc1afcd134e539e7ab3188bd6e1a2cbe07ed9837a1910160405180910390a45050565b60606008805480602002602001604051908101604052809291908181526020015f905b82821015610c05578382905f5260205f2090600202016040518060800160405290815f82015f9054906101000a900460ff166002811115610b2457610b24613cd3565b6002811115610b3557610b35613cd3565b8152815461010081046001600160581b03166020830152600160601b90046001600160a01b03166040820152600182018054606090920191610b76906142dc565b80601f0160208091040260200160405190810160405280929190818152602001828054610ba2906142dc565b8015610bed5780601f10610bc457610100808354040283529160200191610bed565b820191905f5260205f20905b815481529060010190602001808311610bd057829003601f168201915b50505050508152505081526020019060010190610ae1565b50505050905090565b5f61081a6090612fcd565b5f610c2261288f565b6005811115610c3357610c33613cd3565b14158015610c5a57506001610c4661288f565b6005811115610c5757610c57613cd3565b14155b15610c8a57610c6761288f565b60405163eb37a58560e01b8152600401610c8191906142ba565b60405180910390fd5b600480546040516323b872dd60e01b81526001600160a01b03909116916323b872dd91610cbd91339130918791016144e0565b6020604051808303815f875af1158015610cd9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cfd9190614504565b50600580546001600160801b03808216600160801b9283900482168501821683021792839055910416610d308282614533565b6040805133815230602082015284917f3e5931cb7426e4e39150cbb111cc76ae83a5702ccd97fb254c357bcff6b756c591015b60405180910390a450565b610da66040518060a0016040528060608152602001606081526020015f6001600160a01b031681526020015f81526020015f81525090565b60408051606080820183525f808352602080840182905283850182905284518084018652828152808201839052808601839052855180850187528481529182019390935293840152909160408051808201909152606080825260208201526060610e396040518060c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f6040518060a00160405280610e4d61079b565b815260200160038054610e5f906142dc565b80601f0160208091040260200160405190810160405280929190818152602001828054610e8b906142dc565b8015610ed65780601f10610ead57610100808354040283529160200191610ed6565b820191905f5260205f20905b815481529060010190602001808311610eb957829003601f168201915b50505050508152602001610ee8611936565b6001600160a01b03168152602001610efe6116ca565b8152602001610f0b610c0e565b90529750610f17612383565b6001600160a01b0316632c960900610f2d61079b565b6040518263ffffffff1660e01b8152600401610f499190613bde565b606060405180830381865afa158015610f64573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f889190614546565b60408051606080820183526004546001600160a01b03168083526005546001600160801b038082166020860152600160801b909104168385015283519182018085526306fdde0360e01b90529251939a50909850918291906306fdde03906064808501915f918187030181865afa158015611005573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261102c919081019061445d565b815260048054604080516395d89b4160e01b815290516020909401936001600160a01b03909216926395d89b4192828201925f92908290030181865afa158015611078573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261109f919081019061445d565b815260200160045f015f9054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110f6573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061111a91906145be565b815250945060066040518060400160405290815f8201805461113b906142dc565b80601f0160208091040260200160405190810160405280929190818152602001828054611167906142dc565b80156111b25780601f10611189576101008083540402835291602001916111b2565b820191905f5260205f20905b81548152906001019060200180831161119557829003601f168201915b5050505050815260200160018201805480602002602001604051908101604052809291908181526020015f905b8282101561135f575f8481526020908190206040805160a08101825260048602909201805460ff8116845261010090046001600160f81b031693830193909352600183015490820152600282018054919291606084019190611240906142dc565b80601f016020809104026020016040519081016040528092919081815260200182805461126c906142dc565b80156112b75780601f1061128e576101008083540402835291602001916112b7565b820191905f5260205f20905b81548152906001019060200180831161129a57829003601f168201915b505050505081526020016003820180546112d0906142dc565b80601f01602080910402602001604051908101604052809291908181526020018280546112fc906142dc565b80156113475780601f1061131e57610100808354040283529160200191611347565b820191905f5260205f20905b81548152906001019060200180831161132a57829003601f168201915b505050505081525050815260200190600101906111df565b5050509152505060088054604080516020808402820181019092528281529397505f9084015b828210156114a9578382905f5260205f2090600202016040518060800160405290815f82015f9054906101000a900460ff1660028111156113c8576113c8613cd3565b60028111156113d9576113d9613cd3565b8152815461010081046001600160581b03166020830152600160601b90046001600160a01b0316604082015260018201805460609092019161141a906142dc565b80601f0160208091040260200160405190810160405280929190818152602001828054611446906142dc565b80156114915780601f1061146857610100808354040283529160200191611491565b820191905f5260205f20905b81548152906001019060200180831161147457829003601f168201915b50505050508152505081526020019060010190611385565b5050505092505f806114b9611b21565b6040516310cf02b960e01b81523060048201526001600160a01b0391909116906310cf02b9906024016040805180830381865afa1580156114fc573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061152091906145d5565b915091506040518060c001604052805f60029054906101000a90046001600160f01b03166001600160f01b03168152602001600254815260200160015481526020018381526020018281526020016115766103e9565b9052935061158261288f565b925050509091929394959697565b611598612e38565b6001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115d3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115f791906144c5565b6001600160a01b0316336001600160a01b031614611628576040516315cead3960e31b815260040160405180910390fd5b600161163261288f565b600581111561164357611643613cd3565b1461166a5761165061288f565b60405163a3acd01960e01b8152600401610c8191906142ba565b5f611673612e62565b6001600160801b03169050805f60055460408051600160801b9092046001600160801b0316825230602083015233917f4bf986268e3c9b6b83f7e5fc1afcd134e539e7ab3188bd6e1a2cbe07ed9837a19101610d63565b5f61081a6070612fcd565b604080518082019091526060808252602082015260066040518060400160405290815f82018054611705906142dc565b80601f0160208091040260200160405190810160405280929190818152602001828054611731906142dc565b801561177c5780601f106117535761010080835404028352916020019161177c565b820191905f5260205f20905b81548152906001019060200180831161175f57829003601f168201915b5050505050815260200160018201805480602002602001604051908101604052809291908181526020015f905b82821015611929575f8481526020908190206040805160a08101825260048602909201805460ff8116845261010090046001600160f81b03169383019390935260018301549082015260028201805491929160608401919061180a906142dc565b80601f0160208091040260200160405190810160405280929190818152602001828054611836906142dc565b80156118815780601f1061185857610100808354040283529160200191611881565b820191905f5260205f20905b81548152906001019060200180831161186457829003601f168201915b5050505050815260200160038201805461189a906142dc565b80601f01602080910402602001604051908101604052809291908181526020018280546118c6906142dc565b80156119115780601f106118e857610100808354040283529160200191611911565b820191905f5260205f20905b8154815290600101906020018083116118f457829003601f168201915b505050505081525050815260200190600101906117a9565b5050505081525050905090565b5f61081a6028612e3e565b600161194b61288f565b600581111561195c5761195c613cd3565b146119695761165061288f565b5f81900361198a5760405163b931f4df60e01b815260040160405180910390fd5b5f6119936103e9565b905073a35bd0843e49f4ecf7fb716fdaddc86f4482db8e6323b872dd336119b8612e38565b6001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119f3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a1791906144c5565b846040518463ffffffff1660e01b8152600401611a36939291906144e0565b6020604051808303815f875af1158015611a52573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a769190614504565b505f80546201000090046001600160f01b0316906002611a95836145f7565b91906101000a8154816001600160f01b0302191690836001600160f01b0316021790555050611ac2611b21565b6001600160a01b0316639aacb1ea84846040518363ffffffff1660e01b8152600401611aef92919061464c565b5f604051808303815f87803b158015611b06575f80fd5b505af1158015611b18573d5f803e3d5ffd5b50505050505050565b5f611b2a612383565b6001600160a01b0316639b5e6a8b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b65573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061081a91906144c5565b5f805f611b94611b21565b6001600160a01b0316336001600160a01b031614611bc5576040516327a891ff60e01b815260040160405180910390fd5b600a8787604051611bd792919061465f565b9081526020016040518091039020545f03611c015760028054905f611bfb8361466e565b91905055505b8315611c59578460015f828254611c189190614686565b9250508190555084600a8888604051611c3292919061465f565b90815260200160405180910390205f828254611c4e9190614686565b90915550611ce29050565b8460015f828254611c6a9190614533565b9250508190555084600a8888604051611c8492919061465f565b90815260200160405180910390205f828254611ca09190614533565b9091555050604051600a90611cb8908990899061465f565b9081526020016040518091039020545f03611ce25760028054905f611cdc83614699565b91905055505b6001549250600a8787604051611cf992919061465f565b908152602001604051809103902054915060025490509450945094915050565b5f831580611d25575081155b80611d3257506008548414155b80611d3f57506007548214155b15611d5d57604051630ae8117760e21b815260040160405180910390fd5b5f670de0b6b3a764000060608288888281611d7a57611d7a6142c8565b9050602002810190611d8c91906146ae565b611d9a9060208101906146d8565b6002811115611dab57611dab613cd3565b03611dd45750604080518082019091526007815266028a096a0a49d160cd1b6020820152611e60565b6001888885818110611de857611de86142c8565b9050602002810190611dfa91906146ae565b611e089060208101906146d8565b6002811115611e1957611e19613cd3565b03611e415750604080518082019091526006815265028a096a19d160d51b6020820152611e60565b50604080518082019091526006815265028a096ab1d160d51b60208201525b8683101561204857878784818110611e7a57611e7a6142c8565b9050602002810190611e8c91906146ae565b611e9d906040810190602001614707565b6001600160581b03161561204857600981898986818110611ec057611ec06142c8565b9050602002810190611ed291906146ae565b611ee0906060810190614722565b604051602001611ef293929190614764565b60408051601f1981840301815290829052611f0c9161478a565b9081526040519081900360200190205460ff16611f8f5780888885818110611f3657611f366142c8565b9050602002810190611f4891906146ae565b611f56906060810190614722565b604051602001611f6893929190614764565b60408051601f1981840301815290829052633cfbd52760e21b8252610c8191600401613bde565b61203b612034611fe660088681548110611fab57611fab6142c8565b5f918252602090912060029091020154611fda9061010090046001600160581b0316662386f26fc1000061479b565b6001600160581b031690565b61202e8b8b88818110611ffb57611ffb6142c8565b905060200281019061200d91906146ae565b61201e906040810190602001614707565b611fda906509184e72a00061479b565b90612fee565b83906130fc565b6001909301929150611e60565b82871461205a575f935050505061237b565b6040515f935061206f906006906020016147be565b60405160208183030381529060405290505f6120885f90565b90505b858410156122cf576009828888878181106120a8576120a86142c8565b90506020028101906120ba91906147db565b6120c8906060810190614722565b6040516020016120da93929190614764565b60408051601f19818403018152908290526120f49161478a565b9081526040519081900360200190205460ff16612177578187878681811061211e5761211e6142c8565b905060200281019061213091906147db565b61213e906060810190614722565b60405160200161215093929190614764565b60408051601f1981840301815290829052631f3f577f60e21b8252610c8191600401613bde565b868685818110612189576121896142c8565b905060200281019061219b91906147db565b6121ac9060408101906020016147ef565b6001600160f81b0316600660010185815481106121cb576121cb6142c8565b5f91825260209091206004909102015461010090046001600160f81b031611156121f657505f6122cf565b6122c26122bb61227561222b60066001018881548110612218576122186142c8565b905f5260205f2090600402016001015490565b61226f8b8b8a818110612240576122406142c8565b905060200281019061225291906147db565b6122639060408101906020016147ef565b6001600160f81b031690565b90613111565b6122b56006600101888154811061228e5761228e6142c8565b5f9182526020909120600490910201546122b29060ff16662386f26fc1000061480a565b90565b906130fc565b8290613128565b600190940193905061208b565b5f6122dd6122b285846130fc565b90505f816001546122ee9190614686565b6122fb83620186a061480a565b6123059190614835565b6005549091505f90620186a09061232d908490600160801b90046001600160801b031661480a565b6123379190614835565b6005549091506001600160801b03161580159061235f57506005546001600160801b03168110155b1561237257506005546001600160801b03165b96505050505050505b949350505050565b5f61081a6014612e3e565b612396612383565b6001600160a01b0316336001600160a01b0316146123c75760405163cf7527d960e01b815260040160405180910390fd5b60036123d48688836148ab565b508360046123e2828261497c565b5083905060066123f28282614bd6565b9050505f60605b828210156126b8576008848484818110612415576124156142c8565b905060200281019061242791906146ae565b81546001810183555f92835260209092209091600202016124488282614cb3565b505f905084848481811061245e5761245e6142c8565b905060200281019061247091906146ae565b61247e9060208101906146d8565b600281111561248f5761248f613cd3565b036124b85750604080518082019091526007815266028a096a0a49d160cd1b6020820152612544565b60018484848181106124cc576124cc6142c8565b90506020028101906124de91906146ae565b6124ec9060208101906146d8565b60028111156124fd576124fd613cd3565b036125255750604080518082019091526006815265028a096a19d160d51b6020820152612544565b50604080518082019091526006815265028a096ab1d160d51b60208201525b600981858585818110612559576125596142c8565b905060200281019061256b91906146ae565b612579906060810190614722565b60405160200161258b93929190614764565b60408051601f19818403018152908290526125a59161478a565b9081526040519081900360200190205460ff161561262957808484848181106125d0576125d06142c8565b90506020028101906125e291906146ae565b6125f0906060810190614722565b60405160200161260293929190614764565b60408051601f1981840301815290829052634c368ddf60e11b8252610c8191600401613bde565b6001600982868686818110612640576126406142c8565b905060200281019061265291906146ae565b612660906060810190614722565b60405160200161267293929190614764565b60408051601f198184030181529082905261268c9161478a565b908152604051908190036020019020805491151560ff19909216919091179055600191909101906123f9565b5f91505b6126c960208601866149e4565b90508210156128855760096126de8680614722565b6126eb60208901896149e4565b868181106126fb576126fb6142c8565b905060200281019061270d91906147db565b61271b906060810190614722565b60405160200161272e9493929190614d61565b60408051601f19818403018152908290526127489161478a565b9081526040519081900360200190205460ff16156127e15761276a8580614722565b61277760208801886149e4565b85818110612787576127876142c8565b905060200281019061279991906147db565b6127a7906060810190614722565b6040516020016127ba9493929190614d61565b60408051601f19818403018152908290526307bd843d60e41b8252610c8191600401613bde565b600160096127ef8780614722565b6127fc60208a018a6149e4565b8781811061280c5761280c6142c8565b905060200281019061281e91906147db565b61282c906060810190614722565b60405160200161283f9493929190614d61565b60408051601f19818403018152908290526128599161478a565b908152604051908190036020019020805491151560ff19909216919091179055600191909101906126bc565b5050505050505050565b5f6128986116ca565b42116128a357505f90565b6128ab6116ca565b421180156128c057506128bc610c0e565b4211155b80156128e2575060045f5460ff1660058111156128df576128df613cd3565b14155b8015612904575060055f5460ff16600581111561290157612901613cd3565b14155b15612924575f54610100900460ff161561291e5750600290565b50600190565b61292c610c0e565b4211156129395750600390565b505f5460ff1690565b5f61294d6050612fcd565b919050565b60045f5460ff16600581111561296a5761296a613cd3565b14612988576040516306d27d2160e11b815260040160405180910390fd5b5f80612992611b21565b6001600160a01b031663aa8ff04e3086866040518463ffffffff1660e01b81526004016129c193929190614d8b565b608060405180830381865afa1580156129dc573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612a009190614ddf565b93505050915081612a245760405163b931f4df60e01b815260040160405180910390fd5b805f03612a445760405163aebd15cf60e01b815260040160405180910390fd5b600a8484604051612a5692919061465f565b9081526020016040518091039020545f03612a8457604051630169d19960e31b815260040160405180910390fd5b5f600154600a8686604051612a9a92919061465f565b908152602001604051809103902054620186a0612ab7919061480a565b612ac19190614835565b6005549091505f90620186a090612ae9908490600160801b90046001600160801b031661480a565b612af39190614835565b6005549091505f906001600160801b03168103612b7c57600254600114612b625760058054839190601090612b39908490600160801b90046001600160801b0316614e1b565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550612ce1565b600554600160801b90046001600160801b03169150612ce1565b6005546001600160801b03168210612b9d576005546001600160801b031691505b600254600114612c25576005546001600160801b0316821015612bd457600554612bd19083906001600160801b0316614533565b90505b600580546001600160801b038082169291601091612bfc918591600160801b90910416614e1b565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550612c60565b600554600160801b90046001600160801b0316821015612c6057600554612c5d908390600160801b90046001600160801b0316614533565b90505b8015612ce1576004546001600160a01b03166323b872dd30612c8061081f565b846040518463ffffffff1660e01b8152600401612c9f939291906144e0565b6020604051808303815f875af1158015612cbb573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612cdf9190614504565b505b600480546040516323b872dd60e01b81526001600160a01b03909116916323b872dd91612d1491309189918891016144e0565b6020604051808303815f875af1158015612d30573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d549190614504565b50600a8787604051612d6792919061465f565b9081526040519081900360200190205f908190556002805491612d8983614699565b91905055508082856001600160a01b03167fae6eddb2f04f4bdc6d2d987b8dbb9a0b5584769e2428d4d13008d05ac7d8359c8a8a30604051612dcd93929190614e42565b60405180910390a46002545f03611b18575f805460ff1916600590811790915580546001600160801b0316905560405130815242907f4b458c68578d57188b55fc98cef8757c807194fb31378b88df039807cc5159029060200160405180910390a250505050505050565b5f61081a5f5b5f80612e54600119368181013560f01c90030190565b929092013560601c92915050565b5f805460ff191660041781556005546001600160801b031615801590612ea957506005546002546001600160801b03600160801b8304811692612ea79291169061480a565b105b156122b257600554600254612ec7916001600160801b03169061480a565b6004549091506001600160a01b03166323b872dd30612ee461081f565b600554612f02908690600160801b90046001600160801b0316614e1b565b6040516001600160e01b031960e086901b1681526001600160a01b0393841660048201529290911660248301526001600160801b031660448201526064016020604051808303815f875af1158015612f5c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f809190614504565b5060058054829190601090612fa6908490600160801b90046001600160801b0316614e1b565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555090565b5f80612fe3600119368181013560f01c90030190565b929092013592915050565b5f8282818303613017578015613004575f61300e565b670de0b6b3a76400005b925050506130f6565b670de0b6b3a7640000820361303857670de0b6b3a7640000925050506130f6565b805f0361305157670de0b6b3a7640000925050506130f6565b670de0b6b3a7640000810361306a5784925050506130f6565b670de0b6b3a764000082111561309b5761309461308f61308987613136565b866130fc565b61325d565b92506130f3565b5f6130b86122b2846ec097ce7bc90715b34b9f1000000000614835565b90505f6130d061308f6130ca84613136565b886130fc565b90506130ee6122b2826ec097ce7bc90715b34b9f1000000000614835565b945050505b50505b92915050565b5f61310a6122b284846132b1565b9392505050565b5f61310a6122b284670de0b6b3a764000085613363565b5f61310a6122b28385614686565b5f81670de0b6b3a76400008110156131645760405163036d32ef60e41b815260048101849052602401610c81565b5f6131e5670de0b6b3a7640000830460016001600160801b03821160071b91821c6001600160401b03811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c60ff8111600390811b91821c600f811160021b90811c918211871b91821c969096119490961792909217171791909117919091171790565b9050670de0b6b3a7640000810282821c670de0b6b3a763ffff19810161320e5750949350505050565b671bc16d674ec800006706f05b59d3b200005b801561325157670de0b6b3a7640000838002049250818310613249579283019260019290921c915b60011c613221565b50919695505050505050565b5f81680a688906bd8affffff81111561328c5760405163b3b6ba1f60e01b815260048101849052602401610c81565b5f6132a3670de0b6b3a7640000604084901b614835565b905061237b6122b282613431565b5f80805f19848609848602925082811083820303915050805f036132e25750670de0b6b3a7640000900490506130f6565b670de0b6b3a7640000811061331457604051635173648d60e01b81526004810186905260248101859052604401610c81565b5f670de0b6b3a764000085870962040000818503049310909103600160ee1b02919091177faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106690291505092915050565b5f80805f19858709858702925082811083820303915050805f0361339a5783828161339057613390614821565b049250505061310a565b8381106133cb57604051630c740aef60e31b8152600481018790526024810186905260448101859052606401610c81565b5f84868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203025f889003889004909101858311909403939093029303949094049190911702949350505050565b600160bf1b67ff0000000000000082161561353e576780000000000000008216156134655768016a09e667f3bcc9090260401c5b674000000000000000821615613484576801306fe0a31b7152df0260401c5b6720000000000000008216156134a3576801172b83c7d517adce0260401c5b6710000000000000008216156134c25768010b5586cf9890f62a0260401c5b6708000000000000008216156134e1576801059b0d31585743ae0260401c5b67040000000000000082161561350057680102c9a3e778060ee70260401c5b67020000000000000082161561351f5768010163da9fb33356d80260401c5b67010000000000000082161561353e57680100b1afa5abcbed610260401c5b66ff00000000000082161561363d57668000000000000082161561356b5768010058c86da1c09ea20260401c5b6640000000000000821615613589576801002c605e2e8cec500260401c5b66200000000000008216156135a757680100162f3904051fa10260401c5b66100000000000008216156135c5576801000b175effdc76ba0260401c5b66080000000000008216156135e357680100058ba01fb9f96d0260401c5b66040000000000008216156136015768010002c5cc37da94920260401c5b660200000000000082161561361f576801000162e525ee05470260401c5b660100000000000082161561363d5768010000b17255775c040260401c5b65ff00000000008216156137335765800000000000821615613668576801000058b91b5bc9ae0260401c5b6540000000000082161561368557680100002c5c89d5ec6d0260401c5b652000000000008216156136a25768010000162e43f4f8310260401c5b651000000000008216156136bf57680100000b1721bcfc9a0260401c5b650800000000008216156136dc5768010000058b90cf1e6e0260401c5b650400000000008216156136f9576801000002c5c863b73f0260401c5b6502000000000082161561371657680100000162e430e5a20260401c5b65010000000000821615613733576801000000b1721835510260401c5b64ff000000008216156138205764800000000082161561375c57680100000058b90c0b490260401c5b6440000000008216156137785768010000002c5c8601cc0260401c5b642000000000821615613794576801000000162e42fff00260401c5b6410000000008216156137b05768010000000b17217fbb0260401c5b6408000000008216156137cc576801000000058b90bfce0260401c5b6404000000008216156137e857680100000002c5c85fe30260401c5b6402000000008216156138045768010000000162e42ff10260401c5b64010000000082161561382057680100000000b17217f80260401c5b63ff0000008216156139045763800000008216156138475768010000000058b90bfc0260401c5b6340000000821615613862576801000000002c5c85fe0260401c5b632000000082161561387d57680100000000162e42ff0260401c5b6310000000821615613898576801000000000b17217f0260401c5b63080000008216156138b357680100000000058b90c00260401c5b63040000008216156138ce5768010000000002c5c8600260401c5b63020000008216156138e9576801000000000162e4300260401c5b63010000008216156139045768010000000000b172180260401c5b62ff00008216156139df5762800000821615613929576801000000000058b90c0260401c5b6240000082161561394357680100000000002c5c860260401c5b6220000082161561395d5768010000000000162e430260401c5b6210000082161561397757680100000000000b17210260401c5b620800008216156139915768010000000000058b910260401c5b620400008216156139ab576801000000000002c5c80260401c5b620200008216156139c557680100000000000162e40260401c5b620100008216156139df576801000000000000b1720260401c5b61ff00821615613ab157618000821615613a0257680100000000000058b90260401c5b614000821615613a1b5768010000000000002c5d0260401c5b612000821615613a34576801000000000000162e0260401c5b611000821615613a4d5768010000000000000b170260401c5b610800821615613a66576801000000000000058c0260401c5b610400821615613a7f57680100000000000002c60260401c5b610200821615613a9857680100000000000001630260401c5b610100821615613ab157680100000000000000b10260401c5b60ff821615613b7a576080821615613ad257680100000000000000590260401c5b6040821615613aea576801000000000000002c0260401c5b6020821615613b0257680100000000000000160260401c5b6010821615613b1a576801000000000000000b0260401c5b6008821615613b3257680100000000000000060260401c5b6004821615613b4a57680100000000000000030260401c5b6002821615613b6257680100000000000000010260401c5b6001821615613b7a57680100000000000000010260401c5b670de0b6b3a76400000260409190911c60bf031c90565b5f5b83811015613bab578181015183820152602001613b93565b50505f910152565b5f8151808452613bca816020860160208601613b91565b601f01601f19169290920160200192915050565b602081525f61310a6020830184613bb3565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b0381118282101715613c2c57613c2c613bf0565b604052919050565b5f6001600160401b03821115613c4c57613c4c613bf0565b50601f01601f191660200190565b5f60208284031215613c6a575f80fd5b81356001600160401b03811115613c7f575f80fd5b8201601f81018413613c8f575f80fd5b8035613ca2613c9d82613c34565b613c04565b818152856020838501011115613cb6575f80fd5b816020840160208301375f91810160200191909152949350505050565b634e487b7160e01b5f52602160045260245ffd5b5f82825180855260208086019550808260051b8401018186015f5b84811015613d7b57601f1986840301895281516080815160038110613d2957613d29613cd3565b8552818601516001600160581b0316868601526040808301516001600160a01b031690860152606091820151918501819052613d6781860183613bb3565b9a86019a9450505090830190600101613d02565b5090979650505050505050565b602081525f61310a6020830184613ce7565b80516001600160a01b031682526020808201516001600160801b039081169184019190915260409182015116910152565b606081016130f68284613d9a565b5f60208284031215613de9575f80fd5b5035919050565b5f815160608452613e046060850182613bb3565b905060208301518482036020860152613e1d8282613bb3565b915050604083015160408501528091505092915050565b5f60408251818552613e4882860182613bb3565b9050602080850151868303828801528281518085528385019150838160051b86010184840193505f5b82811015613ef257868203601f190184528451805160ff168352868101516001600160f81b031687840152888101518984015260608082015160a08286018190529190613ec083870182613bb3565b9250505060808083015192508482038186015250613ede8183613bb3565b968801969588019593505050600101613e71565b509998505050505050505050565b60068110613f1057613f10613cd3565b9052565b5f6102208083528a5160a082850152613f316102c0850182613bb3565b91505060208b015161021f1984830301610240850152613f518282613bb3565b60408d01516001600160a01b031661026086015260608d015161028086015260808d01516102a08601529150613fb89050602084018b80516001600160401b031682526020808201516001600160a01b039081169184019190915260409182015116910152565b613fc5608084018a613d9a565b82810360e0840152613fd78189613df0565b9050828103610100840152613fec8188613e34565b90508281036101208401526140018187613ce7565b85516101408501526020860151610160850152604086015161018085015260608601516101a085015260808601516101c085015260a08601516101e085015291506140499050565b614057610200830184613f00565b9998505050505050505050565b602081525f61310a6020830184613e34565b5f8083601f840112614086575f80fd5b5081356001600160401b0381111561409c575f80fd5b6020830191508360208285010111156140b3575f80fd5b9250929050565b5f80602083850312156140cb575f80fd5b82356001600160401b038111156140e0575f80fd5b6140ec85828601614076565b90969095509350505050565b8015158114614105575f80fd5b50565b5f805f806060858703121561411b575f80fd5b84356001600160401b03811115614130575f80fd5b61413c87828801614076565b909550935050602085013591506040850135614157816140f8565b939692955090935050565b5f8083601f840112614172575f80fd5b5081356001600160401b03811115614188575f80fd5b6020830191508360208260051b85010111156140b3575f80fd5b5f805f80604085870312156141b5575f80fd5b84356001600160401b03808211156141cb575f80fd5b6141d788838901614162565b909650945060208701359150808211156141ef575f80fd5b506141fc87828801614162565b95989497509550505050565b5f805f805f8086880360c081121561421e575f80fd5b87356001600160401b0380821115614234575f80fd5b6142408b838c01614076565b90995097508791506060601f1984011215614259575f80fd5b60208a01965060808a0135925080831115614272575f80fd5b918901916040838c031215614285575f80fd5b91945060a0890135918083111561429a575f80fd5b50506142a889828a01614162565b979a9699509497509295939492505050565b602081016130f68284613f00565b634e487b7160e01b5f52603260045260245ffd5b600181811c908216806142f057607f821691505b60208210810361430e57634e487b7160e01b5f52602260045260245ffd5b50919050565b5f8154614320816142dc565b60018281168015614338576001811461434d57614379565b60ff1984168752821515830287019450614379565b855f526020805f205f5b858110156143705781548a820152908401908201614357565b50505082870194505b5050505092915050565b5f8351614394818460208801613b91565b6143a081840185614314565b95945050505050565b60ff81168114614105575f80fd5b6001600160f81b0381168114614105575f80fd5b6001600160a01b0381168114614105575f80fd5b5f805f80608085870312156143f2575f80fd5b845160048110614400575f80fd5b6020860151909450614411816143a9565b6040860151909350614422816143b7565b6060860151909250614157816143cb565b5f8351614444818460208801613b91565b6101d160f51b9083019081526143a06002820185614314565b5f6020828403121561446d575f80fd5b81516001600160401b03811115614482575f80fd5b8201601f81018413614492575f80fd5b80516144a0613c9d82613c34565b8181528560208385010111156144b4575f80fd5b6143a0826020830160208601613b91565b5f602082840312156144d5575f80fd5b815161310a816143cb565b6001600160a01b039384168152919092166020820152604081019190915260600190565b5f60208284031215614514575f80fd5b815161310a816140f8565b634e487b7160e01b5f52601160045260245ffd5b818103818111156130f6576130f661451f565b5f60608284031215614556575f80fd5b604051606081016001600160401b03828210818311171561457957614579613bf0565b8160405284519150808216821461458e575f80fd5b508152602083015161459f816143cb565b602082015260408301516145b2816143cb565b60408201529392505050565b5f602082840312156145ce575f80fd5b5051919050565b5f80604083850312156145e6575f80fd5b505080516020909101519092909150565b5f6001600160f01b038281166002600160f01b0319810161461a5761461a61451f565b6001019392505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b602081525f61237b602083018486614624565b818382375f9101908152919050565b5f6001820161467f5761467f61451f565b5060010190565b808201808211156130f6576130f661451f565b5f816146a7576146a761451f565b505f190190565b5f8235607e198336030181126146c2575f80fd5b9190910192915050565b60038110614105575f80fd5b5f602082840312156146e8575f80fd5b813561310a816146cc565b6001600160581b0381168114614105575f80fd5b5f60208284031215614717575f80fd5b813561310a816146f3565b5f808335601e19843603018112614737575f80fd5b8301803591506001600160401b03821115614750575f80fd5b6020019150368190038213156140b3575f80fd5b5f8451614775818460208901613b91565b8201838582375f930192835250909392505050565b5f82516146c2818460208701613b91565b6001600160581b038181168382160280821691908281146130f3576130f361451f565b5f6147c98284614314565b6101d160f51b81526002019392505050565b5f8235609e198336030181126146c2575f80fd5b5f602082840312156147ff575f80fd5b813561310a816143b7565b80820281158282048414176130f6576130f661451f565b634e487b7160e01b5f52601260045260245ffd5b5f8261484f57634e487b7160e01b5f52601260045260245ffd5b500490565b5b81811015614868575f8155600101614855565b5050565b601f8211156148a657805f5260205f20601f840160051c810160208510156148915750805b6148a3601f850160051c830182614854565b50505b505050565b6001600160401b038311156148c2576148c2613bf0565b6148d6836148d083546142dc565b8361486c565b5f601f841160018114614907575f85156148f05750838201355b5f19600387901b1c1916600186901b1783556148a3565b5f83815260209020601f19861690835b828110156149375786850135825560209485019460019092019101614917565b5086821015614953575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b5f81356001600160801b03811681146130f6575f80fd5b8135614987816143cb565b81546001600160a01b0319166001600160a01b03919091161781556001600160801b036149b660208401614965565b166fffffffffffffffffffffffffffffffff196149d560408501614965565b60801b16176001919091015550565b5f808335601e198436030181126149f9575f80fd5b8301803591506001600160401b03821115614a12575f80fd5b6020019150600581901b36038213156140b3575f80fd5b614a3381546142dc565b801561486857601f811160018114614a4c5750505f9055565b5f83815260208120614a69601f850160051c820160018301614854565b508381526020812081855555505050565b8135614a85816143a9565b60ff8116905060ff1981818454161783556020840135614aa4816143b7565b60081b1617815560408201356001820155614ac26060830183614722565b614ad08183600286016148ab565b5050614adf6080830183614722565b614aed8183600386016148ab565b50505050565b68010000000000000000831115614b0c57614b0c613bf0565b805483825580841015614b915760026001600160fe1b038281168314614b3457614b3461451f565b8086168614614b4557614b4561451f565b505f8381526020812086831b81019084841b015b80821015614b8c57828255826001830155614b75848301614a29565b614b8160038301614a29565b600482019150614b59565b505050505b505f8181526020812083915b85811015614bce57614bb8614bb284876147db565b83614a7a565b6020929092019160049190910190600101614b9d565b505050505050565b614be08283614722565b6001600160401b03811115614bf757614bf7613bf0565b614c0b81614c0585546142dc565b8561486c565b5f601f821160018114614c3c575f8315614c255750838201355b5f19600385901b1c1916600184901b178555614c94565b5f85815260209020601f19841690835b82811015614c6c5786850135825560209485019460019092019101614c4c565b5084821015614c88575f1960f88660031b161c19848701351681555b505060018360011b0185555b50505050614ca560208301836149e4565b614aed818360018601614af3565b8135614cbe816146cc565b60038110614cce57614cce613cd3565b815460ff821691508160ff1982161783556020840135614ced816146f3565b6bffffffffffffffffffffff008160081b1690506bffffffffffffffffffffffff198184828516171785556040860135614d26816143cb565b6bffffffffffffffffffffffff9490941691909117606093841b90911617835550614d5390830183614722565b614aed8183600186016148ab565b838582375f8482016101d160f51b8152838560028301375f93016002019283525090949350505050565b6001600160a01b03841681526060602082018190525f90614daf9083018486614624565b828103604093840152600e81526d21a7a72324a39d1020baba3437b960911b602082015291909101949350505050565b5f805f8060808587031215614df2575f80fd5b8451614dfd816140f8565b60208601516040870151606090970151919890975090945092505050565b6001600160801b03828116828216039080821115614e3b57614e3b61451f565b5092915050565b604081525f614e55604083018587614624565b905060018060a01b038316602083015294935050505056fea2646970667358221220ce25312304169e77f08e0ee5708c023a51ea97dc1e1f1dbd30107d76d009e7ce64736f6c63430008140033
Deployed Bytecode
0x608060405234801561000f575f80fd5b5060043610610187575f3560e01c80638c7a8eb3116100d9578063b7701e4411610093578063ef8a92351161006e578063ef8a9235146103b1578063f172a4ce146103c6578063f3fe12c9146103ce578063f851a440146103e1575f80fd5b8063b7701e4414610383578063c45a015514610396578063e80099481461039e575f80fd5b80638c7a8eb3146102df5780638da5cb5b146102f4578063951f8239146102fc578063955c98671461030f578063a7c64c7014610317578063aa588b4b14610345575f80fd5b806326f016371161014457806349c26fcb1161011f57806349c26fcb146102a0578063565974d3146102b357806373e4fc0b146102cf57806378e97925146102d7575f80fd5b806326f01637146102185780633197cbb61461022d5780633326ee9c14610235575f80fd5b806304cbb47f1461018b57806306fdde03146101a65780630cb61f6c146101bb5780630ebf14b5146101db5780631f55479f146101e557806324034a0e146101ed575b5f80fd5b6101936103e9565b6040519081526020015b60405180910390f35b6101ae61079b565b60405161019d9190613bde565b6101c361081f565b6040516001600160a01b03909116815260200161019d565b6101e361082a565b005b6101e3610920565b6101936101fb366004613c5a565b8051602081830181018051600a8252928201919093012091525481565b610220610abe565b60405161019d9190613d88565b610193610c0e565b610293604080516060810182525f808252602082018190529181019190915250604080516060810182526004546001600160a01b031681526005546001600160801b038082166020840152600160801b909104169181019190915290565b60405161019d9190613dcb565b6101e36102ae366004613dd9565b610c19565b6102bb610d6e565b60405161019d989796959493929190613f14565b6101e3611590565b6101936116ca565b6102e76116d5565b60405161019d9190614064565b6101c3611936565b6101e361030a3660046140ba565b611941565b6101c3611b21565b61032a610325366004614108565b611b89565b6040805193845260208401929092529082015260600161019d565b610373610353366004613c5a565b805160208183018101805160098252928201919093012091525460ff1681565b604051901515815260200161019d565b6101936103913660046141a2565b611d19565b6101c3612383565b6101e36103ac366004614208565b61238e565b6103b961288f565b60405161019d91906142ba565b610193612942565b6101e36103dc3660046140ba565b612952565b6101c3612e38565b6008545f9081808060605b848410156105fe575f60088581548110610410576104106142c8565b5f91825260209091206002918202015460ff169081111561043357610433613cd3565b0361045c5750604080518082019091526007815266028a096a0a49d160cd1b60208201526104db565b600160088581548110610471576104716142c8565b5f91825260209091206002918202015460ff169081111561049457610494613cd3565b036104bc5750604080518082019091526006815265028a096a19d160d51b60208201526104db565b50604080518082019091526006815265028a096ab1d160d51b60208201525b6104e3612e38565b6001600160a01b031663b6001e2460088681548110610504576105046142c8565b905f5260205f2090600202015f01600c9054906101000a90046001600160a01b0316836008888154811061053a5761053a6142c8565b905f5260205f20906002020160010160405160200161055a929190614383565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b81526004016105a19291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa1580156105bc573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105e091906143df565b506001909601956001600160f81b03169485019493506103f4915050565b600754600680549196505f955085945090610618906142dc565b80601f0160208091040260200160405190810160405280929190818152602001828054610644906142dc565b801561068f5780601f106106665761010080835404028352916020019161068f565b820191905f5260205f20905b81548152906001019060200180831161067257829003601f168201915b505050505090505b84841015610791576106a7612e38565b6001600160a01b031663b6001e245f83600660010188815481106106cd576106cd6142c8565b905f5260205f2090600402016002016040516020016106ed929190614433565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b81526004016107349291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa15801561074f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061077391906143df565b506001909601956001600160f81b0316948501949350610697915050565b5090949350505050565b60606107a5612383565b6001600160a01b031663fe4d55366107bb612942565b6040518263ffffffff1660e01b81526004016107d991815260200190565b5f60405180830381865afa1580156107f3573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261081a919081019061445d565b905090565b5f61081a603c612e3e565b610832612e38565b6001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa15801561086d573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061089191906144c5565b6001600160a01b0316336001600160a01b0316146108c2576040516315cead3960e31b815260040160405180910390fd5b5f805460ff610100808304821615810261ff00199093169290921792839055604051308152919092049091161515907f771cd98ea23fa6204e979f96eecb381b08abb79f1a85d2a4e7b840086feeb86f9060200160405180910390a2565b5f610929612e38565b6001600160a01b031663b6001e246001604051602001610960907021a7a72324a39d102334b730b634bd32b960791b815260110190565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b81526004016109a79291906001600160a01b03929092168252602082015260400190565b608060405180830381865afa1580156109c2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109e691906143df565b9350505050806001600160a01b0316336001600160a01b031614610a1d576040516389dd324f60e01b815260040160405180910390fd5b6003610a2761288f565b6005811115610a3857610a38613cd3565b14610a56576040516380bb6a1760e01b815260040160405180910390fd5b5f610a5f612e62565b6001600160801b0316905080600160055460408051600160801b9092046001600160801b0316825230602083015233917f4bf986268e3c9b6b83f7e5fc1afcd134e539e7ab3188bd6e1a2cbe07ed9837a1910160405180910390a45050565b60606008805480602002602001604051908101604052809291908181526020015f905b82821015610c05578382905f5260205f2090600202016040518060800160405290815f82015f9054906101000a900460ff166002811115610b2457610b24613cd3565b6002811115610b3557610b35613cd3565b8152815461010081046001600160581b03166020830152600160601b90046001600160a01b03166040820152600182018054606090920191610b76906142dc565b80601f0160208091040260200160405190810160405280929190818152602001828054610ba2906142dc565b8015610bed5780601f10610bc457610100808354040283529160200191610bed565b820191905f5260205f20905b815481529060010190602001808311610bd057829003601f168201915b50505050508152505081526020019060010190610ae1565b50505050905090565b5f61081a6090612fcd565b5f610c2261288f565b6005811115610c3357610c33613cd3565b14158015610c5a57506001610c4661288f565b6005811115610c5757610c57613cd3565b14155b15610c8a57610c6761288f565b60405163eb37a58560e01b8152600401610c8191906142ba565b60405180910390fd5b600480546040516323b872dd60e01b81526001600160a01b03909116916323b872dd91610cbd91339130918791016144e0565b6020604051808303815f875af1158015610cd9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cfd9190614504565b50600580546001600160801b03808216600160801b9283900482168501821683021792839055910416610d308282614533565b6040805133815230602082015284917f3e5931cb7426e4e39150cbb111cc76ae83a5702ccd97fb254c357bcff6b756c591015b60405180910390a450565b610da66040518060a0016040528060608152602001606081526020015f6001600160a01b031681526020015f81526020015f81525090565b60408051606080820183525f808352602080840182905283850182905284518084018652828152808201839052808601839052855180850187528481529182019390935293840152909160408051808201909152606080825260208201526060610e396040518060c001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f6040518060a00160405280610e4d61079b565b815260200160038054610e5f906142dc565b80601f0160208091040260200160405190810160405280929190818152602001828054610e8b906142dc565b8015610ed65780601f10610ead57610100808354040283529160200191610ed6565b820191905f5260205f20905b815481529060010190602001808311610eb957829003601f168201915b50505050508152602001610ee8611936565b6001600160a01b03168152602001610efe6116ca565b8152602001610f0b610c0e565b90529750610f17612383565b6001600160a01b0316632c960900610f2d61079b565b6040518263ffffffff1660e01b8152600401610f499190613bde565b606060405180830381865afa158015610f64573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f889190614546565b60408051606080820183526004546001600160a01b03168083526005546001600160801b038082166020860152600160801b909104168385015283519182018085526306fdde0360e01b90529251939a50909850918291906306fdde03906064808501915f918187030181865afa158015611005573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261102c919081019061445d565b815260048054604080516395d89b4160e01b815290516020909401936001600160a01b03909216926395d89b4192828201925f92908290030181865afa158015611078573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261109f919081019061445d565b815260200160045f015f9054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110f6573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061111a91906145be565b815250945060066040518060400160405290815f8201805461113b906142dc565b80601f0160208091040260200160405190810160405280929190818152602001828054611167906142dc565b80156111b25780601f10611189576101008083540402835291602001916111b2565b820191905f5260205f20905b81548152906001019060200180831161119557829003601f168201915b5050505050815260200160018201805480602002602001604051908101604052809291908181526020015f905b8282101561135f575f8481526020908190206040805160a08101825260048602909201805460ff8116845261010090046001600160f81b031693830193909352600183015490820152600282018054919291606084019190611240906142dc565b80601f016020809104026020016040519081016040528092919081815260200182805461126c906142dc565b80156112b75780601f1061128e576101008083540402835291602001916112b7565b820191905f5260205f20905b81548152906001019060200180831161129a57829003601f168201915b505050505081526020016003820180546112d0906142dc565b80601f01602080910402602001604051908101604052809291908181526020018280546112fc906142dc565b80156113475780601f1061131e57610100808354040283529160200191611347565b820191905f5260205f20905b81548152906001019060200180831161132a57829003601f168201915b505050505081525050815260200190600101906111df565b5050509152505060088054604080516020808402820181019092528281529397505f9084015b828210156114a9578382905f5260205f2090600202016040518060800160405290815f82015f9054906101000a900460ff1660028111156113c8576113c8613cd3565b60028111156113d9576113d9613cd3565b8152815461010081046001600160581b03166020830152600160601b90046001600160a01b0316604082015260018201805460609092019161141a906142dc565b80601f0160208091040260200160405190810160405280929190818152602001828054611446906142dc565b80156114915780601f1061146857610100808354040283529160200191611491565b820191905f5260205f20905b81548152906001019060200180831161147457829003601f168201915b50505050508152505081526020019060010190611385565b5050505092505f806114b9611b21565b6040516310cf02b960e01b81523060048201526001600160a01b0391909116906310cf02b9906024016040805180830381865afa1580156114fc573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061152091906145d5565b915091506040518060c001604052805f60029054906101000a90046001600160f01b03166001600160f01b03168152602001600254815260200160015481526020018381526020018281526020016115766103e9565b9052935061158261288f565b925050509091929394959697565b611598612e38565b6001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115d3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115f791906144c5565b6001600160a01b0316336001600160a01b031614611628576040516315cead3960e31b815260040160405180910390fd5b600161163261288f565b600581111561164357611643613cd3565b1461166a5761165061288f565b60405163a3acd01960e01b8152600401610c8191906142ba565b5f611673612e62565b6001600160801b03169050805f60055460408051600160801b9092046001600160801b0316825230602083015233917f4bf986268e3c9b6b83f7e5fc1afcd134e539e7ab3188bd6e1a2cbe07ed9837a19101610d63565b5f61081a6070612fcd565b604080518082019091526060808252602082015260066040518060400160405290815f82018054611705906142dc565b80601f0160208091040260200160405190810160405280929190818152602001828054611731906142dc565b801561177c5780601f106117535761010080835404028352916020019161177c565b820191905f5260205f20905b81548152906001019060200180831161175f57829003601f168201915b5050505050815260200160018201805480602002602001604051908101604052809291908181526020015f905b82821015611929575f8481526020908190206040805160a08101825260048602909201805460ff8116845261010090046001600160f81b03169383019390935260018301549082015260028201805491929160608401919061180a906142dc565b80601f0160208091040260200160405190810160405280929190818152602001828054611836906142dc565b80156118815780601f1061185857610100808354040283529160200191611881565b820191905f5260205f20905b81548152906001019060200180831161186457829003601f168201915b5050505050815260200160038201805461189a906142dc565b80601f01602080910402602001604051908101604052809291908181526020018280546118c6906142dc565b80156119115780601f106118e857610100808354040283529160200191611911565b820191905f5260205f20905b8154815290600101906020018083116118f457829003601f168201915b505050505081525050815260200190600101906117a9565b5050505081525050905090565b5f61081a6028612e3e565b600161194b61288f565b600581111561195c5761195c613cd3565b146119695761165061288f565b5f81900361198a5760405163b931f4df60e01b815260040160405180910390fd5b5f6119936103e9565b905073a35bd0843e49f4ecf7fb716fdaddc86f4482db8e6323b872dd336119b8612e38565b6001600160a01b031663420f68616040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119f3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a1791906144c5565b846040518463ffffffff1660e01b8152600401611a36939291906144e0565b6020604051808303815f875af1158015611a52573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a769190614504565b505f80546201000090046001600160f01b0316906002611a95836145f7565b91906101000a8154816001600160f01b0302191690836001600160f01b0316021790555050611ac2611b21565b6001600160a01b0316639aacb1ea84846040518363ffffffff1660e01b8152600401611aef92919061464c565b5f604051808303815f87803b158015611b06575f80fd5b505af1158015611b18573d5f803e3d5ffd5b50505050505050565b5f611b2a612383565b6001600160a01b0316639b5e6a8b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b65573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061081a91906144c5565b5f805f611b94611b21565b6001600160a01b0316336001600160a01b031614611bc5576040516327a891ff60e01b815260040160405180910390fd5b600a8787604051611bd792919061465f565b9081526020016040518091039020545f03611c015760028054905f611bfb8361466e565b91905055505b8315611c59578460015f828254611c189190614686565b9250508190555084600a8888604051611c3292919061465f565b90815260200160405180910390205f828254611c4e9190614686565b90915550611ce29050565b8460015f828254611c6a9190614533565b9250508190555084600a8888604051611c8492919061465f565b90815260200160405180910390205f828254611ca09190614533565b9091555050604051600a90611cb8908990899061465f565b9081526020016040518091039020545f03611ce25760028054905f611cdc83614699565b91905055505b6001549250600a8787604051611cf992919061465f565b908152602001604051809103902054915060025490509450945094915050565b5f831580611d25575081155b80611d3257506008548414155b80611d3f57506007548214155b15611d5d57604051630ae8117760e21b815260040160405180910390fd5b5f670de0b6b3a764000060608288888281611d7a57611d7a6142c8565b9050602002810190611d8c91906146ae565b611d9a9060208101906146d8565b6002811115611dab57611dab613cd3565b03611dd45750604080518082019091526007815266028a096a0a49d160cd1b6020820152611e60565b6001888885818110611de857611de86142c8565b9050602002810190611dfa91906146ae565b611e089060208101906146d8565b6002811115611e1957611e19613cd3565b03611e415750604080518082019091526006815265028a096a19d160d51b6020820152611e60565b50604080518082019091526006815265028a096ab1d160d51b60208201525b8683101561204857878784818110611e7a57611e7a6142c8565b9050602002810190611e8c91906146ae565b611e9d906040810190602001614707565b6001600160581b03161561204857600981898986818110611ec057611ec06142c8565b9050602002810190611ed291906146ae565b611ee0906060810190614722565b604051602001611ef293929190614764565b60408051601f1981840301815290829052611f0c9161478a565b9081526040519081900360200190205460ff16611f8f5780888885818110611f3657611f366142c8565b9050602002810190611f4891906146ae565b611f56906060810190614722565b604051602001611f6893929190614764565b60408051601f1981840301815290829052633cfbd52760e21b8252610c8191600401613bde565b61203b612034611fe660088681548110611fab57611fab6142c8565b5f918252602090912060029091020154611fda9061010090046001600160581b0316662386f26fc1000061479b565b6001600160581b031690565b61202e8b8b88818110611ffb57611ffb6142c8565b905060200281019061200d91906146ae565b61201e906040810190602001614707565b611fda906509184e72a00061479b565b90612fee565b83906130fc565b6001909301929150611e60565b82871461205a575f935050505061237b565b6040515f935061206f906006906020016147be565b60405160208183030381529060405290505f6120885f90565b90505b858410156122cf576009828888878181106120a8576120a86142c8565b90506020028101906120ba91906147db565b6120c8906060810190614722565b6040516020016120da93929190614764565b60408051601f19818403018152908290526120f49161478a565b9081526040519081900360200190205460ff16612177578187878681811061211e5761211e6142c8565b905060200281019061213091906147db565b61213e906060810190614722565b60405160200161215093929190614764565b60408051601f1981840301815290829052631f3f577f60e21b8252610c8191600401613bde565b868685818110612189576121896142c8565b905060200281019061219b91906147db565b6121ac9060408101906020016147ef565b6001600160f81b0316600660010185815481106121cb576121cb6142c8565b5f91825260209091206004909102015461010090046001600160f81b031611156121f657505f6122cf565b6122c26122bb61227561222b60066001018881548110612218576122186142c8565b905f5260205f2090600402016001015490565b61226f8b8b8a818110612240576122406142c8565b905060200281019061225291906147db565b6122639060408101906020016147ef565b6001600160f81b031690565b90613111565b6122b56006600101888154811061228e5761228e6142c8565b5f9182526020909120600490910201546122b29060ff16662386f26fc1000061480a565b90565b906130fc565b8290613128565b600190940193905061208b565b5f6122dd6122b285846130fc565b90505f816001546122ee9190614686565b6122fb83620186a061480a565b6123059190614835565b6005549091505f90620186a09061232d908490600160801b90046001600160801b031661480a565b6123379190614835565b6005549091506001600160801b03161580159061235f57506005546001600160801b03168110155b1561237257506005546001600160801b03165b96505050505050505b949350505050565b5f61081a6014612e3e565b612396612383565b6001600160a01b0316336001600160a01b0316146123c75760405163cf7527d960e01b815260040160405180910390fd5b60036123d48688836148ab565b508360046123e2828261497c565b5083905060066123f28282614bd6565b9050505f60605b828210156126b8576008848484818110612415576124156142c8565b905060200281019061242791906146ae565b81546001810183555f92835260209092209091600202016124488282614cb3565b505f905084848481811061245e5761245e6142c8565b905060200281019061247091906146ae565b61247e9060208101906146d8565b600281111561248f5761248f613cd3565b036124b85750604080518082019091526007815266028a096a0a49d160cd1b6020820152612544565b60018484848181106124cc576124cc6142c8565b90506020028101906124de91906146ae565b6124ec9060208101906146d8565b60028111156124fd576124fd613cd3565b036125255750604080518082019091526006815265028a096a19d160d51b6020820152612544565b50604080518082019091526006815265028a096ab1d160d51b60208201525b600981858585818110612559576125596142c8565b905060200281019061256b91906146ae565b612579906060810190614722565b60405160200161258b93929190614764565b60408051601f19818403018152908290526125a59161478a565b9081526040519081900360200190205460ff161561262957808484848181106125d0576125d06142c8565b90506020028101906125e291906146ae565b6125f0906060810190614722565b60405160200161260293929190614764565b60408051601f1981840301815290829052634c368ddf60e11b8252610c8191600401613bde565b6001600982868686818110612640576126406142c8565b905060200281019061265291906146ae565b612660906060810190614722565b60405160200161267293929190614764565b60408051601f198184030181529082905261268c9161478a565b908152604051908190036020019020805491151560ff19909216919091179055600191909101906123f9565b5f91505b6126c960208601866149e4565b90508210156128855760096126de8680614722565b6126eb60208901896149e4565b868181106126fb576126fb6142c8565b905060200281019061270d91906147db565b61271b906060810190614722565b60405160200161272e9493929190614d61565b60408051601f19818403018152908290526127489161478a565b9081526040519081900360200190205460ff16156127e15761276a8580614722565b61277760208801886149e4565b85818110612787576127876142c8565b905060200281019061279991906147db565b6127a7906060810190614722565b6040516020016127ba9493929190614d61565b60408051601f19818403018152908290526307bd843d60e41b8252610c8191600401613bde565b600160096127ef8780614722565b6127fc60208a018a6149e4565b8781811061280c5761280c6142c8565b905060200281019061281e91906147db565b61282c906060810190614722565b60405160200161283f9493929190614d61565b60408051601f19818403018152908290526128599161478a565b908152604051908190036020019020805491151560ff19909216919091179055600191909101906126bc565b5050505050505050565b5f6128986116ca565b42116128a357505f90565b6128ab6116ca565b421180156128c057506128bc610c0e565b4211155b80156128e2575060045f5460ff1660058111156128df576128df613cd3565b14155b8015612904575060055f5460ff16600581111561290157612901613cd3565b14155b15612924575f54610100900460ff161561291e5750600290565b50600190565b61292c610c0e565b4211156129395750600390565b505f5460ff1690565b5f61294d6050612fcd565b919050565b60045f5460ff16600581111561296a5761296a613cd3565b14612988576040516306d27d2160e11b815260040160405180910390fd5b5f80612992611b21565b6001600160a01b031663aa8ff04e3086866040518463ffffffff1660e01b81526004016129c193929190614d8b565b608060405180830381865afa1580156129dc573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612a009190614ddf565b93505050915081612a245760405163b931f4df60e01b815260040160405180910390fd5b805f03612a445760405163aebd15cf60e01b815260040160405180910390fd5b600a8484604051612a5692919061465f565b9081526020016040518091039020545f03612a8457604051630169d19960e31b815260040160405180910390fd5b5f600154600a8686604051612a9a92919061465f565b908152602001604051809103902054620186a0612ab7919061480a565b612ac19190614835565b6005549091505f90620186a090612ae9908490600160801b90046001600160801b031661480a565b612af39190614835565b6005549091505f906001600160801b03168103612b7c57600254600114612b625760058054839190601090612b39908490600160801b90046001600160801b0316614e1b565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550612ce1565b600554600160801b90046001600160801b03169150612ce1565b6005546001600160801b03168210612b9d576005546001600160801b031691505b600254600114612c25576005546001600160801b0316821015612bd457600554612bd19083906001600160801b0316614533565b90505b600580546001600160801b038082169291601091612bfc918591600160801b90910416614e1b565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550612c60565b600554600160801b90046001600160801b0316821015612c6057600554612c5d908390600160801b90046001600160801b0316614533565b90505b8015612ce1576004546001600160a01b03166323b872dd30612c8061081f565b846040518463ffffffff1660e01b8152600401612c9f939291906144e0565b6020604051808303815f875af1158015612cbb573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612cdf9190614504565b505b600480546040516323b872dd60e01b81526001600160a01b03909116916323b872dd91612d1491309189918891016144e0565b6020604051808303815f875af1158015612d30573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d549190614504565b50600a8787604051612d6792919061465f565b9081526040519081900360200190205f908190556002805491612d8983614699565b91905055508082856001600160a01b03167fae6eddb2f04f4bdc6d2d987b8dbb9a0b5584769e2428d4d13008d05ac7d8359c8a8a30604051612dcd93929190614e42565b60405180910390a46002545f03611b18575f805460ff1916600590811790915580546001600160801b0316905560405130815242907f4b458c68578d57188b55fc98cef8757c807194fb31378b88df039807cc5159029060200160405180910390a250505050505050565b5f61081a5f5b5f80612e54600119368181013560f01c90030190565b929092013560601c92915050565b5f805460ff191660041781556005546001600160801b031615801590612ea957506005546002546001600160801b03600160801b8304811692612ea79291169061480a565b105b156122b257600554600254612ec7916001600160801b03169061480a565b6004549091506001600160a01b03166323b872dd30612ee461081f565b600554612f02908690600160801b90046001600160801b0316614e1b565b6040516001600160e01b031960e086901b1681526001600160a01b0393841660048201529290911660248301526001600160801b031660448201526064016020604051808303815f875af1158015612f5c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f809190614504565b5060058054829190601090612fa6908490600160801b90046001600160801b0316614e1b565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555090565b5f80612fe3600119368181013560f01c90030190565b929092013592915050565b5f8282818303613017578015613004575f61300e565b670de0b6b3a76400005b925050506130f6565b670de0b6b3a7640000820361303857670de0b6b3a7640000925050506130f6565b805f0361305157670de0b6b3a7640000925050506130f6565b670de0b6b3a7640000810361306a5784925050506130f6565b670de0b6b3a764000082111561309b5761309461308f61308987613136565b866130fc565b61325d565b92506130f3565b5f6130b86122b2846ec097ce7bc90715b34b9f1000000000614835565b90505f6130d061308f6130ca84613136565b886130fc565b90506130ee6122b2826ec097ce7bc90715b34b9f1000000000614835565b945050505b50505b92915050565b5f61310a6122b284846132b1565b9392505050565b5f61310a6122b284670de0b6b3a764000085613363565b5f61310a6122b28385614686565b5f81670de0b6b3a76400008110156131645760405163036d32ef60e41b815260048101849052602401610c81565b5f6131e5670de0b6b3a7640000830460016001600160801b03821160071b91821c6001600160401b03811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c60ff8111600390811b91821c600f811160021b90811c918211871b91821c969096119490961792909217171791909117919091171790565b9050670de0b6b3a7640000810282821c670de0b6b3a763ffff19810161320e5750949350505050565b671bc16d674ec800006706f05b59d3b200005b801561325157670de0b6b3a7640000838002049250818310613249579283019260019290921c915b60011c613221565b50919695505050505050565b5f81680a688906bd8affffff81111561328c5760405163b3b6ba1f60e01b815260048101849052602401610c81565b5f6132a3670de0b6b3a7640000604084901b614835565b905061237b6122b282613431565b5f80805f19848609848602925082811083820303915050805f036132e25750670de0b6b3a7640000900490506130f6565b670de0b6b3a7640000811061331457604051635173648d60e01b81526004810186905260248101859052604401610c81565b5f670de0b6b3a764000085870962040000818503049310909103600160ee1b02919091177faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106690291505092915050565b5f80805f19858709858702925082811083820303915050805f0361339a5783828161339057613390614821565b049250505061310a565b8381106133cb57604051630c740aef60e31b8152600481018790526024810186905260448101859052606401610c81565b5f84868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203025f889003889004909101858311909403939093029303949094049190911702949350505050565b600160bf1b67ff0000000000000082161561353e576780000000000000008216156134655768016a09e667f3bcc9090260401c5b674000000000000000821615613484576801306fe0a31b7152df0260401c5b6720000000000000008216156134a3576801172b83c7d517adce0260401c5b6710000000000000008216156134c25768010b5586cf9890f62a0260401c5b6708000000000000008216156134e1576801059b0d31585743ae0260401c5b67040000000000000082161561350057680102c9a3e778060ee70260401c5b67020000000000000082161561351f5768010163da9fb33356d80260401c5b67010000000000000082161561353e57680100b1afa5abcbed610260401c5b66ff00000000000082161561363d57668000000000000082161561356b5768010058c86da1c09ea20260401c5b6640000000000000821615613589576801002c605e2e8cec500260401c5b66200000000000008216156135a757680100162f3904051fa10260401c5b66100000000000008216156135c5576801000b175effdc76ba0260401c5b66080000000000008216156135e357680100058ba01fb9f96d0260401c5b66040000000000008216156136015768010002c5cc37da94920260401c5b660200000000000082161561361f576801000162e525ee05470260401c5b660100000000000082161561363d5768010000b17255775c040260401c5b65ff00000000008216156137335765800000000000821615613668576801000058b91b5bc9ae0260401c5b6540000000000082161561368557680100002c5c89d5ec6d0260401c5b652000000000008216156136a25768010000162e43f4f8310260401c5b651000000000008216156136bf57680100000b1721bcfc9a0260401c5b650800000000008216156136dc5768010000058b90cf1e6e0260401c5b650400000000008216156136f9576801000002c5c863b73f0260401c5b6502000000000082161561371657680100000162e430e5a20260401c5b65010000000000821615613733576801000000b1721835510260401c5b64ff000000008216156138205764800000000082161561375c57680100000058b90c0b490260401c5b6440000000008216156137785768010000002c5c8601cc0260401c5b642000000000821615613794576801000000162e42fff00260401c5b6410000000008216156137b05768010000000b17217fbb0260401c5b6408000000008216156137cc576801000000058b90bfce0260401c5b6404000000008216156137e857680100000002c5c85fe30260401c5b6402000000008216156138045768010000000162e42ff10260401c5b64010000000082161561382057680100000000b17217f80260401c5b63ff0000008216156139045763800000008216156138475768010000000058b90bfc0260401c5b6340000000821615613862576801000000002c5c85fe0260401c5b632000000082161561387d57680100000000162e42ff0260401c5b6310000000821615613898576801000000000b17217f0260401c5b63080000008216156138b357680100000000058b90c00260401c5b63040000008216156138ce5768010000000002c5c8600260401c5b63020000008216156138e9576801000000000162e4300260401c5b63010000008216156139045768010000000000b172180260401c5b62ff00008216156139df5762800000821615613929576801000000000058b90c0260401c5b6240000082161561394357680100000000002c5c860260401c5b6220000082161561395d5768010000000000162e430260401c5b6210000082161561397757680100000000000b17210260401c5b620800008216156139915768010000000000058b910260401c5b620400008216156139ab576801000000000002c5c80260401c5b620200008216156139c557680100000000000162e40260401c5b620100008216156139df576801000000000000b1720260401c5b61ff00821615613ab157618000821615613a0257680100000000000058b90260401c5b614000821615613a1b5768010000000000002c5d0260401c5b612000821615613a34576801000000000000162e0260401c5b611000821615613a4d5768010000000000000b170260401c5b610800821615613a66576801000000000000058c0260401c5b610400821615613a7f57680100000000000002c60260401c5b610200821615613a9857680100000000000001630260401c5b610100821615613ab157680100000000000000b10260401c5b60ff821615613b7a576080821615613ad257680100000000000000590260401c5b6040821615613aea576801000000000000002c0260401c5b6020821615613b0257680100000000000000160260401c5b6010821615613b1a576801000000000000000b0260401c5b6008821615613b3257680100000000000000060260401c5b6004821615613b4a57680100000000000000030260401c5b6002821615613b6257680100000000000000010260401c5b6001821615613b7a57680100000000000000010260401c5b670de0b6b3a76400000260409190911c60bf031c90565b5f5b83811015613bab578181015183820152602001613b93565b50505f910152565b5f8151808452613bca816020860160208601613b91565b601f01601f19169290920160200192915050565b602081525f61310a6020830184613bb3565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b0381118282101715613c2c57613c2c613bf0565b604052919050565b5f6001600160401b03821115613c4c57613c4c613bf0565b50601f01601f191660200190565b5f60208284031215613c6a575f80fd5b81356001600160401b03811115613c7f575f80fd5b8201601f81018413613c8f575f80fd5b8035613ca2613c9d82613c34565b613c04565b818152856020838501011115613cb6575f80fd5b816020840160208301375f91810160200191909152949350505050565b634e487b7160e01b5f52602160045260245ffd5b5f82825180855260208086019550808260051b8401018186015f5b84811015613d7b57601f1986840301895281516080815160038110613d2957613d29613cd3565b8552818601516001600160581b0316868601526040808301516001600160a01b031690860152606091820151918501819052613d6781860183613bb3565b9a86019a9450505090830190600101613d02565b5090979650505050505050565b602081525f61310a6020830184613ce7565b80516001600160a01b031682526020808201516001600160801b039081169184019190915260409182015116910152565b606081016130f68284613d9a565b5f60208284031215613de9575f80fd5b5035919050565b5f815160608452613e046060850182613bb3565b905060208301518482036020860152613e1d8282613bb3565b915050604083015160408501528091505092915050565b5f60408251818552613e4882860182613bb3565b9050602080850151868303828801528281518085528385019150838160051b86010184840193505f5b82811015613ef257868203601f190184528451805160ff168352868101516001600160f81b031687840152888101518984015260608082015160a08286018190529190613ec083870182613bb3565b9250505060808083015192508482038186015250613ede8183613bb3565b968801969588019593505050600101613e71565b509998505050505050505050565b60068110613f1057613f10613cd3565b9052565b5f6102208083528a5160a082850152613f316102c0850182613bb3565b91505060208b015161021f1984830301610240850152613f518282613bb3565b60408d01516001600160a01b031661026086015260608d015161028086015260808d01516102a08601529150613fb89050602084018b80516001600160401b031682526020808201516001600160a01b039081169184019190915260409182015116910152565b613fc5608084018a613d9a565b82810360e0840152613fd78189613df0565b9050828103610100840152613fec8188613e34565b90508281036101208401526140018187613ce7565b85516101408501526020860151610160850152604086015161018085015260608601516101a085015260808601516101c085015260a08601516101e085015291506140499050565b614057610200830184613f00565b9998505050505050505050565b602081525f61310a6020830184613e34565b5f8083601f840112614086575f80fd5b5081356001600160401b0381111561409c575f80fd5b6020830191508360208285010111156140b3575f80fd5b9250929050565b5f80602083850312156140cb575f80fd5b82356001600160401b038111156140e0575f80fd5b6140ec85828601614076565b90969095509350505050565b8015158114614105575f80fd5b50565b5f805f806060858703121561411b575f80fd5b84356001600160401b03811115614130575f80fd5b61413c87828801614076565b909550935050602085013591506040850135614157816140f8565b939692955090935050565b5f8083601f840112614172575f80fd5b5081356001600160401b03811115614188575f80fd5b6020830191508360208260051b85010111156140b3575f80fd5b5f805f80604085870312156141b5575f80fd5b84356001600160401b03808211156141cb575f80fd5b6141d788838901614162565b909650945060208701359150808211156141ef575f80fd5b506141fc87828801614162565b95989497509550505050565b5f805f805f8086880360c081121561421e575f80fd5b87356001600160401b0380821115614234575f80fd5b6142408b838c01614076565b90995097508791506060601f1984011215614259575f80fd5b60208a01965060808a0135925080831115614272575f80fd5b918901916040838c031215614285575f80fd5b91945060a0890135918083111561429a575f80fd5b50506142a889828a01614162565b979a9699509497509295939492505050565b602081016130f68284613f00565b634e487b7160e01b5f52603260045260245ffd5b600181811c908216806142f057607f821691505b60208210810361430e57634e487b7160e01b5f52602260045260245ffd5b50919050565b5f8154614320816142dc565b60018281168015614338576001811461434d57614379565b60ff1984168752821515830287019450614379565b855f526020805f205f5b858110156143705781548a820152908401908201614357565b50505082870194505b5050505092915050565b5f8351614394818460208801613b91565b6143a081840185614314565b95945050505050565b60ff81168114614105575f80fd5b6001600160f81b0381168114614105575f80fd5b6001600160a01b0381168114614105575f80fd5b5f805f80608085870312156143f2575f80fd5b845160048110614400575f80fd5b6020860151909450614411816143a9565b6040860151909350614422816143b7565b6060860151909250614157816143cb565b5f8351614444818460208801613b91565b6101d160f51b9083019081526143a06002820185614314565b5f6020828403121561446d575f80fd5b81516001600160401b03811115614482575f80fd5b8201601f81018413614492575f80fd5b80516144a0613c9d82613c34565b8181528560208385010111156144b4575f80fd5b6143a0826020830160208601613b91565b5f602082840312156144d5575f80fd5b815161310a816143cb565b6001600160a01b039384168152919092166020820152604081019190915260600190565b5f60208284031215614514575f80fd5b815161310a816140f8565b634e487b7160e01b5f52601160045260245ffd5b818103818111156130f6576130f661451f565b5f60608284031215614556575f80fd5b604051606081016001600160401b03828210818311171561457957614579613bf0565b8160405284519150808216821461458e575f80fd5b508152602083015161459f816143cb565b602082015260408301516145b2816143cb565b60408201529392505050565b5f602082840312156145ce575f80fd5b5051919050565b5f80604083850312156145e6575f80fd5b505080516020909101519092909150565b5f6001600160f01b038281166002600160f01b0319810161461a5761461a61451f565b6001019392505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b602081525f61237b602083018486614624565b818382375f9101908152919050565b5f6001820161467f5761467f61451f565b5060010190565b808201808211156130f6576130f661451f565b5f816146a7576146a761451f565b505f190190565b5f8235607e198336030181126146c2575f80fd5b9190910192915050565b60038110614105575f80fd5b5f602082840312156146e8575f80fd5b813561310a816146cc565b6001600160581b0381168114614105575f80fd5b5f60208284031215614717575f80fd5b813561310a816146f3565b5f808335601e19843603018112614737575f80fd5b8301803591506001600160401b03821115614750575f80fd5b6020019150368190038213156140b3575f80fd5b5f8451614775818460208901613b91565b8201838582375f930192835250909392505050565b5f82516146c2818460208701613b91565b6001600160581b038181168382160280821691908281146130f3576130f361451f565b5f6147c98284614314565b6101d160f51b81526002019392505050565b5f8235609e198336030181126146c2575f80fd5b5f602082840312156147ff575f80fd5b813561310a816143b7565b80820281158282048414176130f6576130f661451f565b634e487b7160e01b5f52601260045260245ffd5b5f8261484f57634e487b7160e01b5f52601260045260245ffd5b500490565b5b81811015614868575f8155600101614855565b5050565b601f8211156148a657805f5260205f20601f840160051c810160208510156148915750805b6148a3601f850160051c830182614854565b50505b505050565b6001600160401b038311156148c2576148c2613bf0565b6148d6836148d083546142dc565b8361486c565b5f601f841160018114614907575f85156148f05750838201355b5f19600387901b1c1916600186901b1783556148a3565b5f83815260209020601f19861690835b828110156149375786850135825560209485019460019092019101614917565b5086821015614953575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b5f81356001600160801b03811681146130f6575f80fd5b8135614987816143cb565b81546001600160a01b0319166001600160a01b03919091161781556001600160801b036149b660208401614965565b166fffffffffffffffffffffffffffffffff196149d560408501614965565b60801b16176001919091015550565b5f808335601e198436030181126149f9575f80fd5b8301803591506001600160401b03821115614a12575f80fd5b6020019150600581901b36038213156140b3575f80fd5b614a3381546142dc565b801561486857601f811160018114614a4c5750505f9055565b5f83815260208120614a69601f850160051c820160018301614854565b508381526020812081855555505050565b8135614a85816143a9565b60ff8116905060ff1981818454161783556020840135614aa4816143b7565b60081b1617815560408201356001820155614ac26060830183614722565b614ad08183600286016148ab565b5050614adf6080830183614722565b614aed8183600386016148ab565b50505050565b68010000000000000000831115614b0c57614b0c613bf0565b805483825580841015614b915760026001600160fe1b038281168314614b3457614b3461451f565b8086168614614b4557614b4561451f565b505f8381526020812086831b81019084841b015b80821015614b8c57828255826001830155614b75848301614a29565b614b8160038301614a29565b600482019150614b59565b505050505b505f8181526020812083915b85811015614bce57614bb8614bb284876147db565b83614a7a565b6020929092019160049190910190600101614b9d565b505050505050565b614be08283614722565b6001600160401b03811115614bf757614bf7613bf0565b614c0b81614c0585546142dc565b8561486c565b5f601f821160018114614c3c575f8315614c255750838201355b5f19600385901b1c1916600184901b178555614c94565b5f85815260209020601f19841690835b82811015614c6c5786850135825560209485019460019092019101614c4c565b5084821015614c88575f1960f88660031b161c19848701351681555b505060018360011b0185555b50505050614ca560208301836149e4565b614aed818360018601614af3565b8135614cbe816146cc565b60038110614cce57614cce613cd3565b815460ff821691508160ff1982161783556020840135614ced816146f3565b6bffffffffffffffffffffff008160081b1690506bffffffffffffffffffffffff198184828516171785556040860135614d26816143cb565b6bffffffffffffffffffffffff9490941691909117606093841b90911617835550614d5390830183614722565b614aed8183600186016148ab565b838582375f8482016101d160f51b8152838560028301375f93016002019283525090949350505050565b6001600160a01b03841681526060602082018190525f90614daf9083018486614624565b828103604093840152600e81526d21a7a72324a39d1020baba3437b960911b602082015291909101949350505050565b5f805f8060808587031215614df2575f80fd5b8451614dfd816140f8565b60208601516040870151606090970151919890975090945092505050565b6001600160801b03828116828216039080821115614e3b57614e3b61451f565b5092915050565b604081525f614e55604083018587614624565b905060018060a01b038316602083015294935050505056fea2646970667358221220ce25312304169e77f08e0ee5708c023a51ea97dc1e1f1dbd30107d76d009e7ce64736f6c63430008140033
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.