194 STOCK TOKENS · 13 HAVE TICKED · 25 REPRICINGS SCHEDULED

Build

Contracts

Two files. Read-only, ownerless, no fee.

Collapick.sol composes the four numbers into a single call. Nobody else does this: Chainlink knows the token price but not the share price, the token knows the multiplier but not the price, and a router knows the pool but not the multiplier.

Collapick.sol
struct Asset {
    address token;
    uint256 multiplier;   // 1e18, shares represented by one token
    uint256 tokenPrice;   // 1e8, multiplier already inside; 0 when unknown
    uint256 sharePrice;   // 1e8, tokenPrice * 1e18 / multiplier; 0 when unknown
    uint64  updatedAt;    // feed timestamp; 0 when there is no feed
    bool    hasFeed;
}

function tokenCount() external view returns (uint256);
function feedOf(address token) external view returns (address);
function multiplierOf(address token) external view returns (uint256);

function asset(address token) external view returns (Asset memory);
function assets(address[] calldata tokens) external view returns (Asset[] memory);
function all() external view returns (Asset[] memory);

function sharesOf(address token, address stock, uint256 amount)
    external view returns (uint256);
function driftBps(address token, uint256 poolPrice1e8)
    external view returns (int256);
function displayErrorBps(address token) external view returns (int256);
CollapickCalendar.sol
struct Action {
    uint8   kind;              // 0 cash dividend, 1 split, 2 other
    uint64  effectiveAt;       // when the issuer processes it
    uint128 rate1e8;           // per underlying share
    uint64  postedAt;
    uint256 multiplierAtPost;  // what the token read when we posted
    bool    settled;
    uint256 oldMultiplier;     // observed at settlement
    uint256 newMultiplier;     // observed at settlement
    uint64  settledAt;
}

function post(address token, uint8 kind, uint64 effectiveAt, uint128 rate1e8)
    external returns (uint256 index);                       // poster only
function transferPoster(address to) external;               // poster only

function settle(address token, uint256 index) external;     // anyone
function settleAll(address token) external returns (uint256 settledCount);

function poster() external view returns (address);
function trackedCount() external view returns (uint256);
function tracked(uint256 i) external view returns (address);
function actionCount(address token) external view returns (uint256);
function actionAt(address token, uint256 index) external view returns (Action memory);
function actionsOf(address token) external view returns (Action[] memory);
function nextAction(address token) external view returns (Action memory);
function upcoming() external view returns (address[] memory, Action[] memory);

event PosterTransferred(address indexed from, address indexed to);
event ActionPosted(
    address indexed token, uint256 indexed index, uint8 kind, uint64 effectiveAt, uint128 rate1e8
);
event ActionSettled(
    address indexed token, uint256 indexed index, uint256 oldMultiplier, uint256 newMultiplier, uint64 at
);

CollapickRouter.sol is the third, and it exists because of an absence. Every stock token on this chain trades in a Uniswap v3 pool created by a factory of the chain’s own, not the canonical one. The Universal Router deployed here is wired to the canonical factory and cannot address a single one of those pools, and the only contracts that do touch them are market makers’ own, with private selectors. There was no way to trade a stock token from a wallet. The router holds nothing, charges nothing and has no owner; an approval granted to it can only be spent on the swap you asked for.

CollapickRouter.sol
function factory() external view returns (address);
function isPool(address pool) external view returns (bool);

function exactInputSingle(
    address pool,          // must have been made by factory(), checked twice
    bool    zeroForOne,
    uint256 amountIn,
    uint256 minOut,        // the only slippage guard that cannot be lied about
    address recipient,
    uint256 deadline
) external returns (uint256 amountOut);

// Swaps and reverts with the answer: call it with eth_call, never send it.
function quoteExactInputSingle(address pool, bool zeroForOne, uint256 amountIn)
    external returns (uint256 amountOut);

event Swap(
    address indexed trader, address indexed pool, address indexed tokenOut,
    uint256 amountIn, uint256 amountOut
);

The pool argument is checked against the factory on the way in and again inside the swap callback. Without that check a caller could pass a contract of their own and it would be handed a transferFrom against whatever allowance a trader had granted the router. Ten tests run against the live chain before it is allowed to deploy: a 500 USDG round trip comes back as 499.50, which is two crossings of a 0.05% pool and nothing else.

settle() takes no privileges. It reads uiMultiplier() straight off the token and, if it has moved since the action was posted, writes the observed old and new values into the record and into an event. Actions are addressed by index because a token can carry several: the poster owns the forecast, the chain owns the settlement, and nobody can write down a tick that did not happen.