Address Contract Verified
Address
0x885D90A424f87D362C9369C0F3d9A2d28AF495F4
Balance
0 ETH
Nonce
1
Code Size
4227 bytes
Creator
0x632e6920...9989 at tx 0xf0fe9e01...2c697a
Indexed Transactions
0
Contract Bytecode
4227 bytes
0x608060405234801561001057600080fd5b50600436106100b45760003560e01c806364c761171161007157806364c7611714610181578063711bc2cc146101895780639353b9df1461019c578063c2a395941461021a578063ecb16c951461022d578063f0f140561461024057600080fd5b80631287d614146100b957806315497d2c146100ce5780632eb4a7ab146101065780634e71d92d1461013b5780634f2bfe5b1461014357806361d027b31461016e575b600080fd5b6100cc6100c7366004610d77565b610253565b005b6100f16100dc366004610e56565b60046020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b61012d7fbc39affb2a6f4c1e539660ab71ae1554d613a42413e154a6223dd7c868432e5881565b6040519081526020016100fd565b6100cc6103df565b600254610156906001600160a01b031681565b6040516001600160a01b0390911681526020016100fd565b600554610156906001600160a01b031681565b6100cc61051f565b6100cc610197366004610e71565b61081d565b6101e56101aa366004610e56565b600360205260009081526040902080546001909101546001600160401b0380831692600160401b8104821692600160801b9091049091169084565b6040516100fd94939291906001600160401b039485168152928416602084015292166040820152606081019190915260800190565b61012d610228366004610e8a565b610a90565b600154610156906001600160a01b031681565b61012d61024e366004610ecd565b610be4565b336000908152600360205260409020546001600160401b03161561028a5760405163e0e70c4960e01b815260040160405180910390fd5b6040516bffffffffffffffffffffffff193360601b166020820152603481018290526102d090839060540160405160208183030381529060405280519060200120610c83565b6102fc57338183604051630bcc64c960e21b81526004016102f393929190610eef565b60405180910390fd5b60408051608081018252426001600160401b0381168083526000602084015292820192909252606081016103326103e885610f62565b9052336000818152600360209081526040918290208451815486840151878601516001600160401b03908116600160801b0267ffffffffffffffff60801b19928216600160401b026fffffffffffffffffffffffffffffffff1990941691909416179190911716178155606090940151600190940193909355519081527fab67d7ea3f6d1612bda63a01384162c8ee0decb1b35cab6c6ff10dbe0e7d57e0910160405180910390a1505050565b6000546001146104015760405162461bcd60e51b81526004016102f390610f84565b600260009081556104123382610a90565b9050806000036104355760405163be7538fb60e01b815260040160405180910390fd5b3360008181526003602052604090819020805467ffffffffffffffff60801b1916600160801b426001600160401b031602179055600154905163a9059cbb60e01b81526004810192909252602482018390526001600160a01b03169063a9059cbb906044016020604051808303816000875af11580156104b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104dd9190610fa8565b5060408051338152602081018390527f896e034966eaaf1adc54acc0f257056febbd300c9e47182cf761982cf1f5e430910160405180910390a1506001600055565b6000546001146105415760405162461bcd60e51b81526004016102f390610f84565b600260009081556105523382610a90565b9050806000036105755760405163be7538fb60e01b815260040160405180910390fd5b3360009081526003602052604081208054909142916105bc906105a1906001600160401b031684610fd1565b6001600160401b0316670de0b6b3a764000062015180610cb0565b905060006105ce846001015483610be4565b84600101546105dd9190610ff8565b905060006106396813c9647e25a9940000841161062057610609846351a673656509184e72a000610cb0565b61061b90670de0b6b3a7640000610ff8565b61062a565b670d2f13f7789f00005b83670de0b6b3a7640000610cb0565b905060006106478284610ff8565b6001549091506001600160a01b031663a9059cbb336106668a8561100b565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303816000875af11580156106b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106d59190610fa8565b5060015460055460405163a9059cbb60e01b81526001600160a01b0391821660048201526024810185905291169063a9059cbb906044016020604051808303816000875af115801561072b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061074f9190610fa8565b50855477ffffffffffffffffffffffffffffffff00000000000000001916600160801b6001600160401b03871690810267ffffffffffffffff60401b191691909117600160401b9190910217865560408051338152602081018990527f896e034966eaaf1adc54acc0f257056febbd300c9e47182cf761982cf1f5e430910160405180910390a160408051338152602081018390527f5367ccf79b9a526d66835ee11189c8c710ba3ef031629c51977139111d90b953910160405180910390a1505060016000555050505050565b60005460011461083f5760405162461bcd60e51b81526004016102f390610f84565b600260009081553381526004602052604090205460ff16151560010361087857604051637e2f954760e01b815260040160405180910390fd5b6000610885336000610a90565b9050806000036108a85760405163be7538fb60e01b815260040160405180910390fd5b6108b6426305a39a8061100b565b8210156108d65760405163d6cf6c6760e01b815260040160405180910390fd5b336000908152600360205260408120805490914291610902906105a1906001600160401b031684610fd1565b90506000610914846001015483610be4565b84600101546109239190610ff8565b60015460025460405163095ea7b360e01b81526001600160a01b03918216600482015260248101849052929350169063095ea7b3906044016020604051808303816000875af115801561097a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061099e9190610fa8565b50600254604051633e173b2960e01b815233600482015260248101839052604481018890526001600160a01b0390911690633e173b2990606401600060405180830381600087803b1580156109f257600080fd5b505af1158015610a06573d6000803e3d6000fd5b5050336000818152600460209081526040918290208054600160ff19909116179055885467ffffffffffffffff60401b1916600160401b6001600160401b038a1602178955815192835282018590527f8bcae1a205c9e844b0492205ba6ad2b480293ba662b8d7105268451eb7b78d2f935001905060405180910390a15050600160005550505050565b6001600160a01b0382166000908152600360209081526040808320815160808101835281546001600160401b03808216808452600160401b8304821696840196909652600160801b909104169281019290925260010154606082015290808303610b0d5760405163be7538fb60e01b815260040160405180910390fd5b60208201516001600160401b031615610b395760405163bc21cd9360e01b815260040160405180910390fd5b60006001600160401b03851615610b505784610b52565b425b9050816001600160401b0316816001600160401b03161015610b875760405163b7d0949760e01b815260040160405180910390fd5b6000610b966105a18484610fd1565b90506000610bad8560400151846105a19190610fd1565b6060860151909150610bc38161024e8486610ff8565b610bcd8285610be4565b610bd79190610ff8565b9998505050505050505050565b6000683b5c2d7a70fcbc00008210610bfd575081610c7d565b610c24565b808202831515821583830485141716610c1a57600080fd5b9290920492915050565b610c7a68056bc75e2d6310000084670de0b6b3a76400006002610c73670de0b6b3a764000088610c6e680bdf3c4bb0328c0000670de0b6b3a7640000671bc16d674ec80000610c02565b610c02565b0a04610c02565b90505b92915050565b6000610c7a837fbc39affb2a6f4c1e539660ab71ae1554d613a42413e154a6223dd7c868432e5884610ccf565b828202811515841585830485141716610cc857600080fd5b0492915050565b600082610cdc8584610ce5565b14949350505050565b600081815b8451811015610d2a57610d1682868381518110610d0957610d0961101e565b6020026020010151610d32565b915080610d2281611034565b915050610cea565b509392505050565b6000818310610d4e576000828152602084905260409020610c7a565b6000838152602083905260409020610c7a565b634e487b7160e01b600052604160045260246000fd5b60008060408385031215610d8a57600080fd5b82356001600160401b0380821115610da157600080fd5b818501915085601f830112610db557600080fd5b8135602082821115610dc957610dc9610d61565b8160051b604051601f19603f83011681018181108682111715610dee57610dee610d61565b604052928352818301935084810182019289841115610e0c57600080fd5b948201945b83861015610e2a57853585529482019493820193610e11565b9997909101359750505050505050565b80356001600160a01b0381168114610e5157600080fd5b919050565b600060208284031215610e6857600080fd5b610c7a82610e3a565b600060208284031215610e8357600080fd5b5035919050565b60008060408385031215610e9d57600080fd5b610ea683610e3a565b915060208301356001600160401b0381168114610ec257600080fd5b809150509250929050565b60008060408385031215610ee057600080fd5b50508035926020909101359150565b6001600160a01b038416815260208082018490526060604083018190528351908301819052600091848101916080850190845b81811015610f3e57845183529383019391830191600101610f22565b509098975050505050505050565b634e487b7160e01b600052601160045260246000fd5b600082610f7f57634e487b7160e01b600052601260045260246000fd5b500490565b6020808252600a90820152695245454e5452414e435960b01b604082015260600190565b600060208284031215610fba57600080fd5b81518015158114610fca57600080fd5b9392505050565b6001600160401b03828116828216039080821115610ff157610ff1610f4c565b5092915050565b81810381811115610c7d57610c7d610f4c565b80820180821115610c7d57610c7d610f4c565b634e487b7160e01b600052603260045260246000fd5b60006001820161104657611046610f4c565b506001019056fea264697066735822122017a5850431d77d69fe840581eb6e10d56c3dfc7c566613a013d65026447d395364736f6c63430008110033
Verified Source Code Full Match
Compiler: v0.8.17+commit.8df45f5f
EVM: london
Optimization: Yes (200 runs)
BaoDistribution.sol 819 lines
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0;
/**
* @dev These functions deal with verification of Merkle Tree proofs.
*
* The proofs can be generated using the JavaScript library
* https://github.com/miguelmota/merkletreejs[merkletreejs].
* Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
*
* See `test/utils/cryptography/MerkleProof.test.js` for some examples.
*
* WARNING: You should avoid using leaf values that are 64 bytes long prior to
* hashing, or use a hash function other than keccak256 for hashing leaves.
* This is because the concatenation of a sorted pair of internal nodes in
* the merkle tree could be reinterpreted as a leaf value.
*/
library MerkleProof {
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(
bytes32[] memory proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Calldata version of {verify}
*
* _Available since v4.7._
*/
function verifyCalldata(
bytes32[] calldata proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leafs & pre-images are assumed to be sorted.
*
* _Available since v4.4._
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Calldata version of {processProof}
*
* _Available since v4.7._
*/
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if the `leaves` can be proved to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* _Available since v4.7._
*/
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
/**
* @dev Calldata version of {multiProofVerify}
*
* _Available since v4.7._
*/
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and the sibling nodes in `proof`,
* consuming from one or the other at each step according to the instructions given by
* `proofFlags`.
*
* _Available since v4.7._
*/
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the merkle tree.
uint256 leavesLen = leaves.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
return hashes[totalHashes - 1];
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Calldata version of {processMultiProof}
*
* _Available since v4.7._
*/
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the merkle tree.
uint256 leavesLen = leaves.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
return hashes[totalHashes - 1];
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
}
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}
/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
uint256 private locked = 1;
modifier nonReentrant() {
require(locked == 1, "REENTRANCY");
locked = 2;
_;
locked = 1;
}
}
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLibrary {
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
assembly {
// Store x * y in z for now.
z := mul(x, y)
// Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
revert(0, 0)
}
// Divide z by the denominator.
z := div(z, denominator)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
assembly {
// Store x * y in z for now.
z := mul(x, y)
// Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
revert(0, 0)
}
// First, divide z - 1 by the denominator and add 1.
// We allow z - 1 to underflow if z is 0, because we multiply the
// end result by 0 if z is zero, ensuring we return 0 if z is zero.
z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
assembly {
switch x
case 0 {
switch n
case 0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.
let half := shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n := shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n := shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.
// Equivalent to iszero(eq(div(xx, x), x)) here.
if shr(128, x) {
revert(0, 0)
}
// Store x squared.
let xx := mul(x, x)
// Round to the nearest number.
let xxRound := add(xx, half)
// Revert if xx + half overflowed.
if lt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x := div(xxRound, scalar)
// If n is even:
if mod(n, 2) {
// Compute z * x.
let zx := mul(z, x)
// If z * x overflowed:
if iszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.
if iszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.
let zxRound := add(zx, half)
// Revert if zx + half overflowed.
if lt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z := div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/
function sqrt(uint256 x) internal pure returns (uint256 z) {
assembly {
// Start off with z at 1.
z := 1
// Used below to help find a nearby power of 2.
let y := x
// Find the lowest power of 2 that is at least sqrt(x).
if iszero(lt(y, 0x100000000000000000000000000000000)) {
y := shr(128, y) // Like dividing by 2 ** 128.
z := shl(64, z) // Like multiplying by 2 ** 64.
}
if iszero(lt(y, 0x10000000000000000)) {
y := shr(64, y) // Like dividing by 2 ** 64.
z := shl(32, z) // Like multiplying by 2 ** 32.
}
if iszero(lt(y, 0x100000000)) {
y := shr(32, y) // Like dividing by 2 ** 32.
z := shl(16, z) // Like multiplying by 2 ** 16.
}
if iszero(lt(y, 0x10000)) {
y := shr(16, y) // Like dividing by 2 ** 16.
z := shl(8, z) // Like multiplying by 2 ** 8.
}
if iszero(lt(y, 0x100)) {
y := shr(8, y) // Like dividing by 2 ** 8.
z := shl(4, z) // Like multiplying by 2 ** 4.
}
if iszero(lt(y, 0x10)) {
y := shr(4, y) // Like dividing by 2 ** 4.
z := shl(2, z) // Like multiplying by 2 ** 2.
}
if iszero(lt(y, 0x8)) {
// Equivalent to 2 ** z.
z := shl(1, z)
}
// Shifting right by 1 is like dividing by 2.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// Compute a rounded down version of z.
let zRoundDown := div(x, z)
// If zRoundDown is smaller, use it.
if lt(zRoundDown, z) {
z := zRoundDown
}
}
}
}
interface IVotingEscrow {
function create_lock_for(address _to, uint256 _value, uint256 _unlock_time) external;
}
contract BaoDistribution is ReentrancyGuard {
// -------------------------------
// VARIABLES
// -------------------------------
//BaoToken public baoToken;
IERC20 public baoToken;
IVotingEscrow public votingEscrow;
mapping(address => DistInfo) public distributions;
mapping(address => bool) public lockStatus;
address public treasury;
// -------------------------------
// CONSTANTS
// -------------------------------
bytes32 public immutable merkleRoot;
// -------------------------------
// STRUCTS
// -------------------------------
struct DistInfo {
uint64 dateStarted;
uint64 dateEnded;
uint64 lastClaim;
uint256 amountOwedTotal;
}
// -------------------------------
// EVENTS
// -------------------------------
event DistributionStarted(address _account);
event TokensClaimed(address _account, uint256 _amount);
event DistributionEnded(address _account, uint256 _amount);
event DistributionLocked(address _account, uint256 _amount);
// -------------------------------
// CUSTOM ERRORS
// -------------------------------
error DistributionAlreadyStarted();
error DistributionEndedEarly();
error InvalidProof(address _account, uint256 _amount, bytes32[] _proof);
error ZeroClaimable();
error InvalidTimestamp();
error outsideLockRange();
error alreadyLocked();
/**
* Create a new BaoDistribution contract.
*
* @param _baoToken Token to distribute.
* @param _votingEscrow vote escrow BAO contract
* @param _merkleRoot Merkle root to verify accounts' inclusion and amount owed when starting their distribution.
*/
constructor(address _baoToken, address _votingEscrow ,bytes32 _merkleRoot, address _treasury) {
baoToken = IERC20(_baoToken);
votingEscrow = IVotingEscrow(_votingEscrow);
merkleRoot = _merkleRoot;
treasury = _treasury;
}
// -------------------------------
// PUBLIC FUNCTIONS
// -------------------------------
/**
* Starts the distribution of BAO for msg.sender.
*
* @param _proof Merkle proof to verify msg.sender's inclusion and claimed amount.
* @param _amount Amount of tokens msg.sender is owed. Used to generate the merkle tree leaf.
*/
function startDistribution(bytes32[] memory _proof, uint256 _amount) external {
if (distributions[msg.sender].dateStarted != 0) {
revert DistributionAlreadyStarted();
} else if (!verifyProof(_proof, keccak256(abi.encodePacked(msg.sender, _amount)))) {
revert InvalidProof(msg.sender, _amount, _proof);
}
uint64 _now = uint64(block.timestamp);
distributions[msg.sender] = DistInfo(
_now,
0,
_now,
_amount / 1000
);
emit DistributionStarted(msg.sender);
}
/**
* Claim all tokens that have been accrued since msg.sender's last claim.
*/
function claim() external nonReentrant {
uint256 _claimable = claimable(msg.sender, 0);
if (_claimable == 0) {
revert ZeroClaimable();
}
// Update account's DistInfo
distributions[msg.sender].lastClaim = uint64(block.timestamp);
// Send account the tokens that they've accrued since their last claim.
baoToken.transfer(msg.sender, _claimable);
// Emit tokens claimed event for logging
emit TokensClaimed(msg.sender, _claimable);
}
/**
* Claim all tokens that have been accrued since msg.sender's last claim AND
* the rest of the total locked amount owed immediately at a pre-defined slashed rate.
*
* Slash Rate:
* days_since_start <= 365: (100 - .01369863013 * days_since_start)%
* days_since_start > 365: 95%
*/
function endDistribution() external nonReentrant {
uint256 _claimable = claimable(msg.sender, 0);
if (_claimable == 0) {
revert ZeroClaimable();
}
DistInfo storage distInfo = distributions[msg.sender];
uint64 timestamp = uint64(block.timestamp);
uint256 daysSinceStart = FixedPointMathLibrary.mulDivDown(uint256(timestamp - distInfo.dateStarted), 1e18, 86400);
// Calculate total tokens left in distribution after the above claim
uint256 tokensLeft = distInfo.amountOwedTotal - distCurve(distInfo.amountOwedTotal, daysSinceStart);
// Calculate slashed amount
uint256 slash = FixedPointMathLibrary.mulDivDown(
daysSinceStart > 365e18 ? 95e16 : 1e18 - FixedPointMathLibrary.mulDivDown(daysSinceStart, 1369863013, 1e13),
tokensLeft,
1e18
);
uint256 owed = tokensLeft - slash;
// Account gets slashed for (slash / tokensLeft)% of their remaining distribution
baoToken.transfer(msg.sender, owed + _claimable);
// Protocol treasury receives slashed tokens
baoToken.transfer(treasury, slash);
// Update DistInfo storage for account to reflect the end of the account's distribution
distInfo.lastClaim = timestamp;
distInfo.dateEnded = timestamp;
// Emit tokens claimed event for logging
emit TokensClaimed(msg.sender, _claimable);
// Emit distribution ended event for logging
emit DistributionEnded(msg.sender, owed);
}
/**
* Lock all tokens that have NOT been claimed since msg.sender's last claim
*
* The Lock into veBAO will be set at _time with this function in-line with length of distribution curve (minimum of 3 years)
*/
function lockDistribution(uint256 _time) external nonReentrant {
if (lockStatus[msg.sender] == true) {
revert alreadyLocked();
}
uint256 _claimable = claimable(msg.sender, 0);
if (_claimable == 0) {
revert ZeroClaimable();
}
if (_time < block.timestamp + 94608000) {
revert outsideLockRange();
}
DistInfo storage distInfo = distributions[msg.sender];
uint64 timestamp = uint64(block.timestamp);
uint256 daysSinceStart = FixedPointMathLibrary.mulDivDown(uint256(timestamp - distInfo.dateStarted), 1e18, 86400);
// Calculate total tokens left in distribution after the above claim
uint256 tokensLeft = distInfo.amountOwedTotal - distCurve(distInfo.amountOwedTotal, daysSinceStart);
baoToken.approve(address(votingEscrow), tokensLeft);
//lock tokensLeft for msg.sender for _time years (minimum of 3 years)
votingEscrow.create_lock_for(msg.sender, tokensLeft, _time);
lockStatus[msg.sender] = true;
distInfo.dateEnded = timestamp;
emit DistributionLocked(msg.sender, tokensLeft);
}
/**
* Get how many tokens an account is able to claim at a given timestamp. 0 = now.
* This function takes into account the date of the account's last claim, and returns the amount
* of tokens they've accrued since.
*
* @param _account Account address to query.
* @param _timestamp Timestamp to query.
* @return c _account's claimable tokens, scaled by 1e18.
*/
function claimable(address _account, uint64 _timestamp) public view returns (uint256 c) {
DistInfo memory distInfo = distributions[_account];
uint64 dateStarted = distInfo.dateStarted;
if (dateStarted == 0) {
revert ZeroClaimable();
} else if (distInfo.dateEnded != 0) {
revert DistributionEndedEarly();
}
uint64 timestamp = _timestamp == 0 ? uint64(block.timestamp) : _timestamp;
if (timestamp < dateStarted) {
revert InvalidTimestamp();
}
uint256 daysSinceStart = FixedPointMathLibrary.mulDivDown(uint256(timestamp - dateStarted), 1e18, 86400);
uint256 daysSinceClaim = FixedPointMathLibrary.mulDivDown(uint256(timestamp - distInfo.lastClaim), 1e18, 86400);
// Allow the account to claim all tokens accrued since the last time they've claimed.
uint256 _total = distInfo.amountOwedTotal;
c = distCurve(_total, daysSinceStart) - distCurve(_total, daysSinceStart - daysSinceClaim);
}
/**
* Get the amount of tokens that would have been accrued along the distribution curve, assuming _daysSinceStart
* days have passed and the account has never claimed.
*
* f(x) = 0 <= x <= 1095 : (2x/219)^2
*
* @param _amountOwedTotal Total amount of tokens owed, scaled by 1e18.
* @param _daysSinceStart Time since the start of the distribution, scaled by 1e18.
* @return _amount Amount of tokens accrued on the distribution curve, assuming the time passed is _daysSinceStart.
*/
function distCurve(uint256 _amountOwedTotal, uint256 _daysSinceStart) public pure returns (uint256 _amount) {
if (_daysSinceStart >= 1095e18) return _amountOwedTotal;
assembly {
// Solmate's mulDivDown function
function mulDivDown(x, y, denominator) -> z {
// Store x * y in z for now.
z := mul(x, y)
// Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
revert(0, 0)
}
// Divide z by the denominator.
z := div(z, denominator)
}
// This is disgusting, but its more gas efficient than storing the results in `_amount` each time.
_amount := mulDivDown( // Multiply `amountOwedTotal` by distribution curve result
div( // Correct precision after exponent op (scale down by 1e20 instead of 1e18 to convert % to a proportion)
exp( // Raise result to the power of two
mulDivDown( // (2/219) * `_daysSinceStart`
mulDivDown(0x1BC16D674EC80000, 0xDE0B6B3A7640000, 0xBDF3C4BB0328C0000),
_daysSinceStart,
0xDE0B6B3A7640000
),
2
),
0xDE0B6B3A7640000
),
_amountOwedTotal,
0x56BC75E2D63100000
)
}
}
// -------------------------------
// PRIVATE FUNCTIONS
// -------------------------------
/**
* Verifies a merkle proof against the stored root.
*
* @param _proof Merkle proof.
* @param _leaf Leaf to verify.
* @return bool True if proof is valid, false if proof is invalid.
*/
function verifyProof(bytes32[] memory _proof, bytes32 _leaf) private view returns (bool) {
return MerkleProof.verify(_proof, merkleRoot, _leaf);
}
}
Read Contract
baoToken 0xecb16c95 → address
claimable 0xc2a39594 → uint256
distCurve 0xf0f14056 → uint256
distributions 0x9353b9df → uint64, uint64, uint64, uint256
lockStatus 0x15497d2c → bool
merkleRoot 0x2eb4a7ab → bytes32
treasury 0x61d027b3 → address
votingEscrow 0x4f2bfe5b → address
Write Contract 4 functions
These functions modify contract state and require a wallet transaction to execute.
claim 0x4e71d92d
No parameters
endDistribution 0x64c76117
No parameters
lockDistribution 0x711bc2cc
uint256 _time
startDistribution 0x1287d614
bytes32[] _proof
uint256 _amount
Recent Transactions
No transactions found for this address