Overview
ETH Balance
0 ETH
More Info
ContractCreator
TokenTracker
Multichain Info
N/A
| Transaction Hash |
Method
|
Block
|
From
|
To
|
Amount
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
To
|
Amount
|
||
|---|---|---|---|---|---|---|---|
| 0x6100883d | 5653112 | 584 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Minimal Proxy Contract for 0xc2039502aa6d154a13f1ade7a05dbd48c962e73b
Contract Name:
LS1155CloneImpl
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 200 runs
Other Settings:
london EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.23;
import {Owned} from "solmate/auth/Owned.sol";
import {ERC1155, ERC1155TokenReceiver} from "solmate/tokens/ERC1155.sol";
import {LibString} from "solmate/utils/LibString.sol";
import {Base64} from "solady/utils/Base64.sol";
import {LiquidSplitCloneImpl} from "src/CloneImpl/LiquidSplitCloneImpl.sol";
import {Renderer} from "src/libs/Renderer.sol";
import {utils} from "src/libs/Utils.sol";
/// @title 1155LiquidSplit
/// @author 0xSplits
/// @notice A minimal liquid split implementation designed to be used as part of a
/// clones-with-immutable-args implementation.
/// Ownership in a split is represented by 1155s (each = 0.1% of split)
/// @dev This contract uses token = address(0) to refer to ETH.
contract LS1155CloneImpl is Owned, LiquidSplitCloneImpl, ERC1155, ERC1155TokenReceiver {
/// -----------------------------------------------------------------------
/// errors
/// -----------------------------------------------------------------------
/// Unauthorized msg.sender
error Unauthorized();
/// Array lengths of accounts & percentAllocations don't match (`accountsLength` != `allocationsLength`)
/// @param accountsLength Length of accounts array
/// @param allocationsLength Length of percentAllocations array
error InvalidLiquidSplit__AccountsAndAllocationsMismatch(uint256 accountsLength, uint256 allocationsLength);
/// Invalid initAllocations sum `allocationsSum` must equal `TOTAL_SUPPLY`
/// @param allocationsSum Sum of percentAllocations array
error InvalidLiquidSplit__InvalidAllocationsSum(uint32 allocationsSum);
/// -----------------------------------------------------------------------
/// libraries
/// -----------------------------------------------------------------------
using LibString for uint256;
/// -----------------------------------------------------------------------
/// storage
/// -----------------------------------------------------------------------
uint256 internal constant TOKEN_ID = 0;
uint256 public constant TOTAL_SUPPLY = 1e3;
uint256 public constant SUPPLY_TO_PERCENTAGE = 1e3; // = PERCENTAGE_SCALE / TOTAL_SUPPLY = 1e6 / 1e3
/// -----------------------------------------------------------------------
/// storage - cwia
/// -----------------------------------------------------------------------
// first item is uint32 distributorFee in LiquidSplitCloneImpl
// 4; second item
uint256 internal constant MINTED_ON_TIMESTAMP_OFFSET = 4;
/// @dev equivalent to uint256 public immutable mintedOnTimestamp;
function mintedOnTimestamp() public pure returns (uint256) {
return _getArgUint256(MINTED_ON_TIMESTAMP_OFFSET);
}
/// -----------------------------------------------------------------------
/// constructor & initializer
/// -----------------------------------------------------------------------
/// only run when implementation is deployed
constructor(address _splitMain) Owned(address(0)) LiquidSplitCloneImpl(_splitMain) {}
/// initializes each clone
function initializer(address[] calldata accounts, uint32[] calldata initAllocations, address _owner) external {
/// checks
// only liquidSplitFactory may call `initializer`
if (msg.sender != liquidSplitFactory) {
revert Unauthorized();
}
if (accounts.length != initAllocations.length) {
revert InvalidLiquidSplit__AccountsAndAllocationsMismatch(accounts.length, initAllocations.length);
}
{
uint32 sum = _getSum(initAllocations);
if (sum != TOTAL_SUPPLY) {
revert InvalidLiquidSplit__InvalidAllocationsSum(sum);
}
}
/// effects
owner = _owner;
emit OwnershipTransferred(address(0), _owner);
LiquidSplitCloneImpl.initializer();
/// interactions
// mint NFTs to initial holders
uint256 numAccs = accounts.length;
unchecked {
for (uint256 i; i < numAccs; ++i) {
_mint({to: accounts[i], id: TOKEN_ID, amount: initAllocations[i], data: ""});
}
}
}
/// -----------------------------------------------------------------------
/// functions
/// -----------------------------------------------------------------------
/// -----------------------------------------------------------------------
/// functions - public & external
/// -----------------------------------------------------------------------
/// -----------------------------------------------------------------------
/// functions - public & external - view & pure
/// -----------------------------------------------------------------------
function scaledPercentBalanceOf(address account) public view override returns (uint32) {
unchecked {
// can't overflow;
// sum(balanceOf) == TOTAL_SUPPLY = 1e3
// SUPPLY_TO_PERCENTAGE = 1e6 / 1e3 = 1e3
// =>
// sum(balanceOf[i] * SUPPLY_TO_PERCENTAGE) == PERCENTAGE_SCALE = 1e6 << 2^32)
return uint32(balanceOf[account][TOKEN_ID] * SUPPLY_TO_PERCENTAGE);
}
}
function name() external view returns (string memory) {
return string.concat("Liquid Split ", utils.shortAddressToString(address(this)));
}
function uri(uint256) public view override returns (string memory) {
return string.concat(
"data:application/json;base64,",
Base64.encode(
bytes(
string.concat(
'{"name": "Liquid Split ',
utils.shortAddressToString(address(this)),
'", "description": ',
'"Each token represents 0.1% of this Liquid Split.", ',
'"external_url": ',
'"https://app.0xsplits.xyz/accounts/',
utils.addressToString(address(this)),
"/?chainId=",
utils.uint2str(block.chainid),
'", ',
'"image": "data:image/svg+xml;base64,',
Base64.encode(bytes(Renderer.render(address(this)))),
'"}'
)
)
)
);
}
/// -----------------------------------------------------------------------
/// functions - private & internal
/// -----------------------------------------------------------------------
/// -----------------------------------------------------------------------
/// functions - private & internal - pure
/// -----------------------------------------------------------------------
/// Sums array of uint32s
/// @param numbers Array of uint32s to sum
/// @return sum Sum of `numbers`
function _getSum(uint32[] calldata numbers) internal pure returns (uint32 sum) {
uint256 numbersLength = numbers.length;
for (uint256 i; i < numbersLength;) {
sum += numbers[i];
unchecked {
// overflow should be impossible in for-loop index
++i;
}
}
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event OwnershipTransferred(address indexed user, address indexed newOwner);
/*//////////////////////////////////////////////////////////////
OWNERSHIP STORAGE
//////////////////////////////////////////////////////////////*/
address public owner;
modifier onlyOwner() virtual {
require(msg.sender == owner, "UNAUTHORIZED");
_;
}
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(address _owner) {
owner = _owner;
emit OwnershipTransferred(address(0), _owner);
}
/*//////////////////////////////////////////////////////////////
OWNERSHIP LOGIC
//////////////////////////////////////////////////////////////*/
function transferOwnership(address newOwner) public virtual onlyOwner {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Minimalist and gas efficient standard ERC1155 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
abstract contract ERC1155 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event TransferSingle(
address indexed operator,
address indexed from,
address indexed to,
uint256 id,
uint256 amount
);
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] amounts
);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
event URI(string value, uint256 indexed id);
/*//////////////////////////////////////////////////////////////
ERC1155 STORAGE
//////////////////////////////////////////////////////////////*/
mapping(address => mapping(uint256 => uint256)) public balanceOf;
mapping(address => mapping(address => bool)) public isApprovedForAll;
/*//////////////////////////////////////////////////////////////
METADATA LOGIC
//////////////////////////////////////////////////////////////*/
function uri(uint256 id) public view virtual returns (string memory);
/*//////////////////////////////////////////////////////////////
ERC1155 LOGIC
//////////////////////////////////////////////////////////////*/
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes calldata data
) public virtual {
require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
balanceOf[from][id] -= amount;
balanceOf[to][id] += amount;
emit TransferSingle(msg.sender, from, to, id, amount);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) ==
ERC1155TokenReceiver.onERC1155Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) public virtual {
require(ids.length == amounts.length, "LENGTH_MISMATCH");
require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
// Storing these outside the loop saves ~15 gas per iteration.
uint256 id;
uint256 amount;
for (uint256 i = 0; i < ids.length; ) {
id = ids[i];
amount = amounts[i];
balanceOf[from][id] -= amount;
balanceOf[to][id] += amount;
// An array can't have a total length
// larger than the max uint256 value.
unchecked {
++i;
}
}
emit TransferBatch(msg.sender, from, to, ids, amounts);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) ==
ERC1155TokenReceiver.onERC1155BatchReceived.selector,
"UNSAFE_RECIPIENT"
);
}
function balanceOfBatch(address[] calldata owners, uint256[] calldata ids)
public
view
virtual
returns (uint256[] memory balances)
{
require(owners.length == ids.length, "LENGTH_MISMATCH");
balances = new uint256[](owners.length);
// Unchecked because the only math done is incrementing
// the array index counter which cannot possibly overflow.
unchecked {
for (uint256 i = 0; i < owners.length; ++i) {
balances[i] = balanceOf[owners[i]][ids[i]];
}
}
}
/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155
interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(
address to,
uint256 id,
uint256 amount,
bytes memory data
) internal virtual {
balanceOf[to][id] += amount;
emit TransferSingle(msg.sender, address(0), to, id, amount);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) ==
ERC1155TokenReceiver.onERC1155Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _batchMint(
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {
uint256 idsLength = ids.length; // Saves MLOADs.
require(idsLength == amounts.length, "LENGTH_MISMATCH");
for (uint256 i = 0; i < idsLength; ) {
balanceOf[to][ids[i]] += amounts[i];
// An array can't have a total length
// larger than the max uint256 value.
unchecked {
++i;
}
}
emit TransferBatch(msg.sender, address(0), to, ids, amounts);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) ==
ERC1155TokenReceiver.onERC1155BatchReceived.selector,
"UNSAFE_RECIPIENT"
);
}
function _batchBurn(
address from,
uint256[] memory ids,
uint256[] memory amounts
) internal virtual {
uint256 idsLength = ids.length; // Saves MLOADs.
require(idsLength == amounts.length, "LENGTH_MISMATCH");
for (uint256 i = 0; i < idsLength; ) {
balanceOf[from][ids[i]] -= amounts[i];
// An array can't have a total length
// larger than the max uint256 value.
unchecked {
++i;
}
}
emit TransferBatch(msg.sender, from, address(0), ids, amounts);
}
function _burn(
address from,
uint256 id,
uint256 amount
) internal virtual {
balanceOf[from][id] -= amount;
emit TransferSingle(msg.sender, from, address(0), id, amount);
}
}
/// @notice A generic interface for a contract which properly accepts ERC1155 tokens.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
abstract contract ERC1155TokenReceiver {
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return ERC1155TokenReceiver.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] calldata,
uint256[] calldata,
bytes calldata
) external virtual returns (bytes4) {
return ERC1155TokenReceiver.onERC1155BatchReceived.selector;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/// @notice Efficient library for creating string representations of integers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
/// @author Modified from Solady (https://github.com/Vectorized/solady/blob/main/src/utils/LibString.sol)
library LibString {
function toString(uint256 value) internal pure returns (string memory str) {
assembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but we allocate 160 bytes
// to keep the free memory pointer word aligned. We'll need 1 word for the length, 1 word for the
// trailing zeros padding, and 3 other words for a max of 78 digits. In total: 5 * 32 = 160 bytes.
let newFreeMemoryPointer := add(mload(0x40), 160)
// Update the free memory pointer to avoid overriding our string.
mstore(0x40, newFreeMemoryPointer)
// Assign str to the end of the zone of newly allocated memory.
str := sub(newFreeMemoryPointer, 32)
// Clean the last word of memory it may not be overwritten.
mstore(str, 0)
// Cache the end of the memory to calculate the length later.
let end := str
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
// prettier-ignore
for { let temp := value } 1 {} {
// Move the pointer 1 byte to the left.
str := sub(str, 1)
// Write the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(str, add(48, mod(temp, 10)))
// Keep dividing temp until zero.
temp := div(temp, 10)
// prettier-ignore
if iszero(temp) { break }
}
// Compute and cache the final total length of the string.
let length := sub(end, str)
// Move the pointer 32 bytes leftwards to make room for the length.
str := sub(str, 32)
// Store the string's length at the start of memory allocated for our string.
mstore(str, length)
}
}
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library to encode strings in Base64. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol) /// @author Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos - <[email protected]>. library Base64 { /// @dev Encodes `data` using the base64 encoding described in RFC 4648. /// See: https://datatracker.ietf.org/doc/html/rfc4648 /// @param fileSafe Whether to replace '+' with '-' and '/' with '_'. /// @param noPadding Whether to strip away the padding. function encode( bytes memory data, bool fileSafe, bool noPadding ) internal pure returns (string memory result) { assembly { let dataLength := mload(data) if dataLength { // Multiply by 4/3 rounded up. // The `shl(2, ...)` is equivalent to multiplying by 4. let encodedLength := shl(2, div(add(dataLength, 2), 3)) // Set `result` to point to the start of the free memory. result := mload(0x40) // Store the table into the scratch space. // Offsetted by -1 byte so that the `mload` will load the character. // We will rewrite the free memory pointer at `0x40` later with // the allocated size. // The magic constant 0x0230 will translate "-_" + "+/". mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef") mstore(0x3f, sub("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0230))) // Skip the first slot, which stores the length. let ptr := add(result, 0x20) let end := add(ptr, encodedLength) // Run over the input, 3 bytes at a time. // prettier-ignore for {} 1 {} { data := add(data, 3) // Advance 3 bytes. let input := mload(data) // Write 4 bytes. Optimized for fewer stack operations. mstore8( ptr , mload(and(shr(18, input), 0x3F))) mstore8(add(ptr, 1), mload(and(shr(12, input), 0x3F))) mstore8(add(ptr, 2), mload(and(shr( 6, input), 0x3F))) mstore8(add(ptr, 3), mload(and( input , 0x3F))) ptr := add(ptr, 4) // Advance 4 bytes. // prettier-ignore if iszero(lt(ptr, end)) { break } } let r := mod(dataLength, 3) switch noPadding case 0 { // Offset `ptr` and pad with '='. We can simply write over the end. mstore8(sub(ptr, iszero(iszero(r))), 0x3d) // Pad at `ptr - 1` if `r > 0`. mstore8(sub(ptr, shl(1, eq(r, 1))), 0x3d) // Pad at `ptr - 2` if `r == 1`. // Write the length of the string. mstore(result, encodedLength) } default { // Write the length of the string. mstore(result, sub(encodedLength, add(iszero(iszero(r)), eq(r, 1)))) } // Allocate the memory for the string. // Add 31 and mask with `not(31)` to round the // free memory pointer up the next multiple of 32. mstore(0x40, and(add(end, 31), not(31))) } } } /// @dev Encodes `data` using the base64 encoding described in RFC 4648. /// Equivalent to `encode(data, false, false)`. function encode(bytes memory data) internal pure returns (string memory result) { result = encode(data, false, false); } /// @dev Encodes `data` using the base64 encoding described in RFC 4648. /// Equivalent to `encode(data, fileSafe, false)`. function encode(bytes memory data, bool fileSafe) internal pure returns (string memory result) { result = encode(data, fileSafe, false); } /// @dev Encodes base64 encoded `data`. /// /// Supports: /// - RFC 4648 (both standard and file-safe mode). /// - RFC 3501 (63: ','). /// /// Does not support: /// - Line breaks. /// /// Note: For performance reasons, /// this function will NOT revert on invalid `data` inputs. /// Outputs for invalid inputs will simply be undefined behaviour. /// It is the user's responsibility to ensure that the `data` /// is a valid base64 encoded string. function decode(string memory data) internal pure returns (bytes memory result) { assembly { let dataLength := mload(data) if dataLength { let end := add(data, dataLength) let decodedLength := mul(shr(2, dataLength), 3) switch and(dataLength, 3) case 0 { // If padded. decodedLength := sub( decodedLength, add(eq(and(mload(end), 0xFF), 0x3d), eq(and(mload(end), 0xFFFF), 0x3d3d)) ) } default { // If non-padded. decodedLength := add(decodedLength, sub(and(dataLength, 3), 1)) } result := mload(0x40) // Write the length of the string. mstore(result, decodedLength) // Skip the first slot, which stores the length. let ptr := add(result, 0x20) // Load the table into the scratch space. // Constants are optimized for smaller bytecode with zero gas overhead. // `m` also doubles as the mask of the upper 6 bits. let m := 0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8cc mstore(0x5b, m) mstore(0x3b, 0x04080c1014181c2024282c3034383c4044484c5054585c6064) mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4) // prettier-ignore for {} 1 {} { // Read 4 bytes. data := add(data, 4) let input := mload(data) // Write 3 bytes. mstore(ptr, or( and(m, mload(byte(28, input))), shr(6, or( and(m, mload(byte(29, input))), shr(6, or( and(m, mload(byte(30, input))), shr(6, mload(byte(31, input))) )) )) )) ptr := add(ptr, 3) // prettier-ignore if iszero(lt(data, end)) { break } } // Allocate the memory for the string. // Add 32 + 31 and mask with `not(31)` to round the // free memory pointer up the next multiple of 32. mstore(0x40, and(add(add(result, decodedLength), 63), not(31))) // Restore the zero slot. mstore(0x60, 0) } } } }
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.23;
import {ERC20} from "solmate/tokens/ERC20.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
import {Clone} from "solady/utils/Clone.sol";
import {ISplitMain} from "src/interfaces/ISplitMain.sol";
/// @title LiquidSplitCloneImpl
/// @author 0xSplits
/// @notice An abstract liquid split base designed to be used as part of a
/// clones-with-immutable-args implementation.
/// @dev This contract uses token = address(0) to refer to ETH.
abstract contract LiquidSplitCloneImpl is Clone {
/// -----------------------------------------------------------------------
/// libraries
/// -----------------------------------------------------------------------
using SafeTransferLib for address;
/// -----------------------------------------------------------------------
/// events
/// -----------------------------------------------------------------------
/// Emitted after each successful ETH transfer to proxy
/// @param amount Amount of ETH received
/// @dev embedded in & emitted from clone bytecode
event ReceiveETH(uint256 amount);
/// -----------------------------------------------------------------------
/// storage
/// -----------------------------------------------------------------------
address internal constant ETH_ADDRESS = address(0);
uint256 public constant PERCENTAGE_SCALE = 1e6;
ISplitMain public immutable splitMain;
address internal immutable liquidSplitFactory;
address public payoutSplit;
/// -----------------------------------------------------------------------
/// storage - cwia
/// -----------------------------------------------------------------------
// 0; first item
uint256 internal constant DISTRIBUTOR_FEE_OFFSET = 0;
/// @dev equivalent to uint32 public immutable distributorFee;
function distributorFee() public pure returns (uint32) {
return _getArgUint32(DISTRIBUTOR_FEE_OFFSET);
}
/// -----------------------------------------------------------------------
/// constructor & initializer
/// -----------------------------------------------------------------------
constructor(address _splitMain) {
splitMain = ISplitMain(_splitMain);
liquidSplitFactory = msg.sender;
}
/// @dev cannot be called externally by default; inheriting contracts must
/// be sure to properly secure any calls
function initializer() internal {
/// checks
/// effects
/// interactions
// create dummy mutable split with this contract as controller;
// recipients & distributorFee will be updated on first payout
address[] memory recipients = new address[](2);
recipients[0] = address(0);
recipients[1] = address(1);
uint32[] memory initPercentAllocations = new uint32[](2);
initPercentAllocations[0] = uint32(500000);
initPercentAllocations[1] = uint32(500000);
payoutSplit = payable(
splitMain.createSplit({
accounts: recipients,
percentAllocations: initPercentAllocations,
distributorFee: 0,
controller: address(this)
})
);
}
/// -----------------------------------------------------------------------
/// functions
/// -----------------------------------------------------------------------
/// -----------------------------------------------------------------------
/// functions - public & external
/// -----------------------------------------------------------------------
/// emit event when receiving ETH
/// @dev implemented w/i clone bytecode
/* receive() external virtual payable { */
/* emit ReceiveETH(msg.value); */
/* } */
/// distributes ETH & ERC20s to NFT holders
/// @param token ETH (0x0) or ERC20 token to distribute
/// @param accounts Ordered, unique list of NFT holders
/// @param distributorAddress Address to receive distributorFee
function distributeFunds(address token, address[] calldata accounts, address distributorAddress) external virtual {
uint256 numRecipients = accounts.length;
uint32[] memory percentAllocations = new uint32[](numRecipients);
for (uint256 i; i < numRecipients;) {
percentAllocations[i] = scaledPercentBalanceOf(accounts[i]);
unchecked {
++i;
}
}
// atomically deposit funds, update recipients to reflect current NFT holders, and distribute
if (token == ETH_ADDRESS) {
payoutSplit.safeTransferETH(address(this).balance);
splitMain.updateAndDistributeETH({
split: payoutSplit,
accounts: accounts,
percentAllocations: percentAllocations,
distributorFee: distributorFee(),
distributorAddress: distributorAddress
});
} else {
token.safeTransfer(payoutSplit, ERC20(token).balanceOf(address(this)));
splitMain.updateAndDistributeERC20({
split: payoutSplit,
token: ERC20(token),
accounts: accounts,
percentAllocations: percentAllocations,
distributorFee: distributorFee(),
distributorAddress: distributorAddress
});
}
}
/// -----------------------------------------------------------------------
/// functions - public & external - view & pure
/// -----------------------------------------------------------------------
function scaledPercentBalanceOf(address account) public view virtual returns (uint32) {}
/// @dev Reads an immutable arg with type uint32.
function _getArgUint32(uint256 argOffset) internal pure returns (uint32 arg) {
uint256 offset = _getImmutableArgsOffset();
assembly {
arg := shr(0xe0, calldataload(add(offset, argOffset)))
}
}
}//SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import "./SVG.sol";
import "./Utils.sol";
// adapted from https://github.com/w1nt3r-eth/hot-chain-svg
library Renderer {
uint256 internal constant size = 600;
uint256 internal constant rowSpacing = size / 8;
uint256 internal constant colSpacing = size / 14;
uint256 internal constant MAX_R = colSpacing * 65 / 100;
uint256 internal constant MIN_R = colSpacing * 3 / 10;
uint256 internal constant maxDur = 60;
uint256 internal constant minDur = 30;
uint256 internal constant durRandomnessDiscord = 5; // (60 - 30) < 2^5
function render(address addr) internal pure returns (string memory) {
string memory logo;
uint256 seed = uint256(uint160(addr));
string memory color = utils.getHslColor(seed);
uint8[5] memory xs = [5, 4, 3, 4, 5];
uint256 y = rowSpacing * 2;
for (uint256 i; i < 5; i++) {
uint256 x = colSpacing * xs[i];
for (uint256 j; j < (8 - xs[i]); j++) {
logo = string.concat(logo, drawRandomOrb(x, y, color, seed = newSeed(seed)));
x += colSpacing * 2;
}
y += rowSpacing;
}
return string.concat(
'<svg xmlns="http://www.w3.org/2000/svg" width="',
utils.uint2str(size),
'" height="',
utils.uint2str(size),
'" style="background:#000000;font-family:sans-serif;fill:#fafafa;font-size:32">',
logo,
"</svg>"
);
}
function randomR(uint256 seed) internal pure returns (uint256 r) {
r = utils.bound(seed, MAX_R, MIN_R);
}
function randomDur(uint256 seed) internal pure returns (uint256 dur) {
dur = utils.bound(seed, maxDur, minDur);
}
function newSeed(uint256 seed) internal pure returns (uint256) {
return uint256(keccak256(abi.encodePacked(seed)));
}
function drawRandomOrb(uint256 cx, uint256 cy, string memory color, uint256 seed)
internal
pure
returns (string memory)
{
uint256 dur = randomDur(seed);
uint256 r = randomR(seed >> durRandomnessDiscord);
return drawOrb(cx, cy, r, dur, color);
}
function drawOrb(uint256 cx, uint256 cy, uint256 r, uint256 dur, string memory color)
internal
pure
returns (string memory _values)
{
string memory animate;
string memory durStr = string.concat(utils.uint2str(dur / 10), ".", utils.uint2str(dur % 10));
string memory valStr = string.concat(
utils.uint2str(r), "; ", utils.uint2str(MAX_R), "; ", utils.uint2str(MIN_R), "; ", utils.uint2str(r)
);
animate = svg.animate(
string.concat(
svg.prop("attributeName", "r"),
svg.prop("dur", durStr),
svg.prop("repeatCount", "indefinite"),
svg.prop("calcMode", "paced"),
svg.prop("values", valStr)
)
);
_values = svg.circle(
string.concat(
svg.prop("cx", utils.uint2str(cx)),
svg.prop("cy", utils.uint2str(cy)),
svg.prop("r", utils.uint2str(r)),
svg.prop("fill", color)
),
animate
);
}
}//SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
// adapted from https://github.com/w1nt3r-eth/hot-chain-svg
// Core utils used extensively to format CSS and numbers.
library utils {
// used to simulate empty strings
string internal constant NULL = "";
// formats a CSS variable line. includes a semicolon for formatting.
function setCssVar(string memory _key, string memory _val) internal pure returns (string memory) {
return string.concat("--", _key, ":", _val, ";");
}
// formats getting a css variable
function getCssVar(string memory _key) internal pure returns (string memory) {
return string.concat("var(--", _key, ")");
}
// formats getting a def URL
function getDefURL(string memory _id) internal pure returns (string memory) {
return string.concat("url(#", _id, ")");
}
// formats rgba white with a specified opacity / alpha
function white_a(uint256 _a) internal pure returns (string memory) {
return rgba(255, 255, 255, _a);
}
// formats rgba black with a specified opacity / alpha
function black_a(uint256 _a) internal pure returns (string memory) {
return rgba(0, 0, 0, _a);
}
// formats generic rgba color in css
function rgba(uint256 _r, uint256 _g, uint256 _b, uint256 _a) internal pure returns (string memory) {
string memory formattedA = _a < 100 ? string.concat("0.", utils.uint2str(_a)) : "1";
return string.concat(
"rgba(", utils.uint2str(_r), ",", utils.uint2str(_g), ",", utils.uint2str(_b), ",", formattedA, ")"
);
}
// checks if two strings are equal
function stringsEqual(string memory _a, string memory _b) internal pure returns (bool) {
return keccak256(abi.encodePacked(_a)) == keccak256(abi.encodePacked(_b));
}
// returns the length of a string in characters
function utfStringLength(string memory _str) internal pure returns (uint256 length) {
uint256 i = 0;
bytes memory string_rep = bytes(_str);
while (i < string_rep.length) {
if (string_rep[i] >> 7 == 0) {
i += 1;
} else if (string_rep[i] >> 5 == bytes1(uint8(0x6))) {
i += 2;
} else if (string_rep[i] >> 4 == bytes1(uint8(0xE))) {
i += 3;
} else if (string_rep[i] >> 3 == bytes1(uint8(0x1E))) {
i += 4;
}
//For safety
else {
i += 1;
}
length++;
}
}
// converts an unsigned integer to a string
function uint2str(uint256 _i) internal pure returns (string memory _uintAsString) {
if (_i == 0) {
return "0";
}
uint256 j = _i;
uint256 len;
while (j != 0) {
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
uint256 k = len;
while (_i != 0) {
k = k - 1;
uint8 temp = (48 + uint8(_i - (_i / 10) * 10));
bytes1 b1 = bytes1(temp);
bstr[k] = b1;
_i /= 10;
}
return string(bstr);
}
// generate hsla color from seed
function getHslColor(uint256 seed) internal pure returns (string memory _hsla) {
uint256 hue = seed % 360;
_hsla = string.concat("hsla(", utils.uint2str(hue), ", 88%, 56%, 1)");
}
function bound(uint256 value, uint256 max, uint256 min) internal pure returns (uint256 _value) {
/* require(max >= min, "INVALID_BOUND"); */
_value = value % (max - min) + min;
}
function addressToString(address _address) internal pure returns (string memory) {
bytes32 _bytes = bytes32(uint256(uint160(_address)));
bytes memory HEX = "0123456789abcdef";
bytes memory _string = new bytes(42);
_string[0] = "0";
_string[1] = "x";
for (uint256 i = 0; i < 20; i++) {
_string[2 + i * 2] = HEX[uint8(_bytes[i + 12] >> 4)];
_string[3 + i * 2] = HEX[uint8(_bytes[i + 12] & 0x0f)];
}
return string(_string);
}
function shortAddressToString(address _address) internal pure returns (string memory) {
bytes32 _bytes = bytes32(uint256(uint160(_address)));
bytes memory HEX = "0123456789abcdef";
bytes memory _string = new bytes(6);
_string[0] = "0";
_string[1] = "x";
for (uint256 i = 0; i < 2; i++) {
_string[2 + i * 2] = HEX[uint8(_bytes[i + 12] >> 4)];
_string[3 + i * 2] = HEX[uint8(_bytes[i + 12] & 0x0f)];
}
return string(_string);
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.
library SafeTransferLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
error ETHTransferFailed();
error TransferFromFailed();
error TransferFailed();
error ApproveFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ETH OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function safeTransferETH(address to, uint256 amount) internal {
assembly {
// Transfer the ETH and check if it succeeded or not.
if iszero(call(gas(), to, amount, 0, 0, 0, 0)) {
// Store the function selector of `ETHTransferFailed()`.
mstore(0x00, 0xb12d13eb)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function safeTransferFrom(
address token,
address from,
address to,
uint256 amount
) internal {
assembly {
// We'll write our calldata to this slot below, but restore it later.
let memPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(0x00, 0x23b872dd)
mstore(0x20, from) // Append the "from" argument.
mstore(0x40, to) // Append the "to" argument.
mstore(0x60, amount) // Append the "amount" argument.
if iszero(
and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(eq(mload(0x00), 1), iszero(returndatasize())),
// We use 0x64 because that's the total length of our calldata (0x04 + 0x20 * 3)
// Counterintuitively, this call() must be positioned after the or() in the
// surrounding and() because and() evaluates its arguments from right to left.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
// Store the function selector of `TransferFromFailed()`.
mstore(0x00, 0x7939f424)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, memPointer) // Restore the memPointer.
}
}
function safeTransfer(
address token,
address to,
uint256 amount
) internal {
assembly {
// We'll write our calldata to this slot below, but restore it later.
let memPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(0x00, 0xa9059cbb)
mstore(0x20, to) // Append the "to" argument.
mstore(0x40, amount) // Append the "amount" argument.
if iszero(
and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(eq(mload(0x00), 1), iszero(returndatasize())),
// We use 0x44 because that's the total length of our calldata (0x04 + 0x20 * 2)
// Counterintuitively, this call() must be positioned after the or() in the
// surrounding and() because and() evaluates its arguments from right to left.
call(gas(), token, 0, 0x1c, 0x44, 0x00, 0x20)
)
) {
// Store the function selector of `TransferFailed()`.
mstore(0x00, 0x90b8ec18)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
mstore(0x40, memPointer) // Restore the memPointer.
}
}
function safeApprove(
address token,
address to,
uint256 amount
) internal {
assembly {
// We'll write our calldata to this slot below, but restore it later.
let memPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(0x00, 0x095ea7b3)
mstore(0x20, to) // Append the "to" argument.
mstore(0x40, amount) // Append the "amount" argument.
if iszero(
and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(eq(mload(0x00), 1), iszero(returndatasize())),
// We use 0x44 because that's the total length of our calldata (0x04 + 0x20 * 2)
// Counterintuitively, this call() must be positioned after the or() in the
// surrounding and() because and() evaluates its arguments from right to left.
call(gas(), token, 0, 0x1c, 0x44, 0x00, 0x20)
)
) {
// Store the function selector of `ApproveFailed()`.
mstore(0x00, 0x3e3f8f73)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
mstore(0x40, memPointer) // Restore the memPointer.
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Class with helper read functions for clone with immutable args.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Clone.sol)
/// @author Adapted from clones with immutable args by zefram.eth, Saw-mon & Natalie
/// (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args)
abstract contract Clone {
/// @dev Reads an immutable arg with type bytes.
function _getArgBytes(uint256 argOffset, uint256 length) internal pure returns (bytes memory arg) {
uint256 offset = _getImmutableArgsOffset();
assembly {
// Grab the free memory pointer.
arg := mload(0x40)
// Store the array length.
mstore(arg, length)
// Copy the array.
calldatacopy(add(arg, 0x20), add(offset, argOffset), length)
// Allocate the memory, rounded up to the next 32 byte boudnary.
mstore(0x40, and(add(add(arg, 0x3f), length), not(0x1f)))
}
}
/// @dev Reads an immutable arg with type address.
function _getArgAddress(uint256 argOffset) internal pure returns (address arg) {
uint256 offset = _getImmutableArgsOffset();
assembly {
arg := shr(0x60, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint256
function _getArgUint256(uint256 argOffset) internal pure returns (uint256 arg) {
uint256 offset = _getImmutableArgsOffset();
assembly {
arg := calldataload(add(offset, argOffset))
}
}
/// @dev Reads a uint256 array stored in the immutable args.
function _getArgUint256Array(uint256 argOffset, uint256 length) internal pure returns (uint256[] memory arg) {
uint256 offset = _getImmutableArgsOffset();
assembly {
// Grab the free memory pointer.
arg := mload(0x40)
// Store the array length.
mstore(arg, length)
// Copy the array.
calldatacopy(add(arg, 0x20), add(offset, argOffset), shl(5, length))
// Allocate the memory.
mstore(0x40, add(add(arg, 0x20), shl(5, length)))
}
}
/// @dev Reads an immutable arg with type uint64.
function _getArgUint64(uint256 argOffset) internal pure returns (uint64 arg) {
uint256 offset = _getImmutableArgsOffset();
assembly {
arg := shr(0xc0, calldataload(add(offset, argOffset)))
}
}
/// @dev Reads an immutable arg with type uint8.
function _getArgUint8(uint256 argOffset) internal pure returns (uint8 arg) {
uint256 offset = _getImmutableArgsOffset();
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) {
assembly {
offset := sub(calldatasize(), shr(0xf0, calldataload(sub(calldatasize(), 2))))
}
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.23;
import {ERC20} from "solmate/tokens/ERC20.sol";
interface ISplitMain {
error InvalidSplit__TooFewAccounts(uint256 accountsLength);
function createSplit(
address[] calldata accounts,
uint32[] calldata percentAllocations,
uint32 distributorFee,
address controller
) external returns (address);
function updateAndDistributeETH(
address split,
address[] calldata accounts,
uint32[] calldata percentAllocations,
uint32 distributorFee,
address distributorAddress
) external;
function updateAndDistributeERC20(
address split,
ERC20 token,
address[] calldata accounts,
uint32[] calldata percentAllocations,
uint32 distributorFee,
address distributorAddress
) external;
function getETHBalance(address account) external view returns (uint256);
function getERC20Balance(address account, ERC20 token) external view returns (uint256);
function withdraw(address account, uint256 withdrawETH, ERC20[] calldata tokens) external;
}//SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import "./Utils.sol";
// from https://github.com/w1nt3r-eth/hot-chain-svg
// Core SVG utilitiy library which helps us construct
// onchain SVG's with a simple, web-like API.
library svg {
/* MAIN ELEMENTS */
function g(string memory _props, string memory _children) internal pure returns (string memory) {
return el("g", _props, _children);
}
function path(string memory _props, string memory _children) internal pure returns (string memory) {
return el("path", _props, _children);
}
function text(string memory _props, string memory _children) internal pure returns (string memory) {
return el("text", _props, _children);
}
function line(string memory _props, string memory _children) internal pure returns (string memory) {
return el("line", _props, _children);
}
function circle(string memory _props, string memory _children) internal pure returns (string memory) {
return el("circle", _props, _children);
}
function circle(string memory _props) internal pure returns (string memory) {
return el("circle", _props);
}
function rect(string memory _props, string memory _children) internal pure returns (string memory) {
return el("rect", _props, _children);
}
function rect(string memory _props) internal pure returns (string memory) {
return el("rect", _props);
}
function filter(string memory _props, string memory _children) internal pure returns (string memory) {
return el("filter", _props, _children);
}
function cdata(string memory _content) internal pure returns (string memory) {
return string.concat("<![CDATA[", _content, "]]>");
}
/* GRADIENTS */
function radialGradient(string memory _props, string memory _children) internal pure returns (string memory) {
return el("radialGradient", _props, _children);
}
function linearGradient(string memory _props, string memory _children) internal pure returns (string memory) {
return el("linearGradient", _props, _children);
}
function gradientStop(uint256 offset, string memory stopColor, string memory _props)
internal
pure
returns (string memory)
{
return el(
"stop",
string.concat(
prop("stop-color", stopColor),
" ",
prop("offset", string.concat(utils.uint2str(offset), "%")),
" ",
_props
)
);
}
function animate(string memory _props) internal pure returns (string memory) {
return el("animate", _props);
}
function animateTransform(string memory _props) internal pure returns (string memory) {
return el("animateTransform", _props);
}
function image(string memory _href, string memory _props) internal pure returns (string memory) {
return el("image", string.concat(prop("href", _href), " ", _props));
}
/* COMMON */
// A generic element, can be used to construct any SVG (or HTML) element
function el(string memory _tag, string memory _props, string memory _children)
internal
pure
returns (string memory)
{
return string.concat("<", _tag, " ", _props, ">", _children, "</", _tag, ">");
}
// A generic element, can be used to construct any SVG (or HTML) element without children
function el(string memory _tag, string memory _props) internal pure returns (string memory) {
return string.concat("<", _tag, " ", _props, "/>");
}
// an SVG attribute
function prop(string memory _key, string memory _val) internal pure returns (string memory) {
return string.concat(_key, "=", '"', _val, '" ');
}
}{
"remappings": [
"forge-std/=lib/forge-std/src/",
"solmate/=lib/solmate/src/",
"solady/=lib/solady/src/",
"splits-contracts/=lib/splits-contracts/contracts/",
"ds-test/=lib/solmate/lib/ds-test/src/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "london",
"libraries": {}
}Contract ABI
API[{"inputs":[{"internalType":"address","name":"_splitMain","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"accountsLength","type":"uint256"},{"internalType":"uint256","name":"allocationsLength","type":"uint256"}],"name":"InvalidLiquidSplit__AccountsAndAllocationsMismatch","type":"error"},{"inputs":[{"internalType":"uint32","name":"allocationsSum","type":"uint32"}],"name":"InvalidLiquidSplit__InvalidAllocationsSum","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ReceiveETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"inputs":[],"name":"PERCENTAGE_SCALE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SUPPLY_TO_PERCENTAGE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOTAL_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"owners","type":"address[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"balances","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"address","name":"distributorAddress","type":"address"}],"name":"distributeFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distributorFee","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint32[]","name":"initAllocations","type":"uint32[]"},{"internalType":"address","name":"_owner","type":"address"}],"name":"initializer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintedOnTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"payoutSplit","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeBatchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"scaledPercentBalanceOf","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"splitMain","outputs":[{"internalType":"contract ISplitMain","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.