# LaunchBlock v3 — launch + bundle in one transaction (Pons V2, Robinhood Chain)

`launchBundle()` launches a Pons V2 token and, **in the same transaction**, buys it for up to 31 recipient wallets that are declared snipe-tax exempt in the launch itself. No wallet funding, no timing race, no burned buys (a curve buy sequenced before its launch is a plain value transfer to a codeless address), no 99% snipe tax. The contract holds nothing between transactions.

**Footprint.** v3 emits no events, keeps no counters and no registry, and runs every launch through a one-time executor clone that self-destructs in the same transaction (EIP-6780). The Pons factory event therefore names an address that has no code afterwards, not LaunchBlock, and no topic query lists LaunchBlock launches. What remains visible is inherent: the transaction to LaunchBlock in your wallet's history, the launch's exempt list (your bundle wallets), and, if you opt into a fee vault, the vault as the token's deployer. With the vault off (default) your wallet is the deployer, exactly as in a direct Pons launch.

Optional: route the token's creator fees through a per-launch **FeeVault**. Anyone can call `harvest()` for a bounty; the creator gets the rest minus a protocol split fixed at launch. Fees keep flowing to the creator with zero maintenance.

| | |
|---|---|
| LaunchBlock v3 | `0x35458585776446cd6169F908cc24e3Fa1B44ebFD` (deprecated: v1 `0x9a42e1d2a377fcb81d44a1223815208a4f98ba00` with events, v2 `0x52b82f51f6547e84844e627f8cb4cf84a6e336ae`) |
| FeeVault implementation | `0xcb91bfe111cdfcda83e50e172f176b945d97caa5` |
| Chain | Robinhood Chain, id 4663, RPC `https://rpc.mainnet.chain.robinhood.com` |
| Pons forwarder / escrow | `0xe33e9e479df8802cb0866d5d05258bec4cf62948` / `0xd3AFEB2a57f70eF218Aa82451c51B2fb0416Ac9e` |
| Build | solc 0.8.26, optimizer 200 runs, viaIR, cancun. Source in `contracts/LaunchBlock.sol`, ABI in `abi.json`. |
| Fees | service fee `100` bps of the bundled ETH (cap 300 in code); FeeVault split `1000` bps of creator fees (cap 2000); harvest bounty `100` bps, cap `0.02` ETH |

## Call

```
quote(seed, amounts[], gasDrop) -> (total, bundled, fee)          // send exactly `total` (overpayment is refunded)
launchBundle(LaunchParams p, Bundle b) payable -> (token, curve, vault)

struct LaunchParams { string name; string symbol; string logo; string description; Socials socials;  // Pons params, unchanged (a description containing a URL is hidden by the Pons site)
                      address deployer; uint16 creatorTaxBps; bool flag; bytes32 salt1; bytes32 salt2; }
struct Socials { string a; string b; string c; string d; string e; }                                   // Pons order: twitter, telegram, discord, website, farcaster
struct Bundle { uint256 seed; uint256 minSeedOut; address[] recipients; uint256[] amounts;
                uint256 minOutEach; uint256 gasDrop; bool useVault; }
```

- `p.deployer` must be your address, unless `b.useVault` (then LaunchBlock puts the new FeeVault there).
- `seed` is the launch dev-buy; its tokens go to you. The forwarder's own launch fee (0.0005 ETH) is included in `quote`.
- `recipients` get the bundle buys and `gasDrop` ETH each, so they can sell later without a funding trail from you.
- Use a gasDrop of at least 0.0003 ETH: a later approve + sell costs ~0.00005 ETH at today's gas, and the recipients hold nothing else.
- Every recipient buy is a real `CurveBuy` on the new curve, exempt from the snipe tax because the recipient is in the exempt list.

### Python (web3.py)

```python
from web3 import Web3
w3 = Web3(Web3.HTTPProvider("https://rpc.mainnet.chain.robinhood.com"))
lb = w3.eth.contract(address="0x35458585776446cd6169F908cc24e3Fa1B44ebFD", abi=ABI)                    # abi.json
me = acct.address
params = ("Name", "SYM", "ipfs://<image cid>", "one line", ("https://x.com/…", "https://t.me/…", "", "https://site", ""),
          me, 0, False, b"\x00"*32, os.urandom(32))              # socials = (twitter, telegram, discord, website, farcaster); deployer = me (LaunchBlock overrides it when useVault)
recipients = [w.address for w in my_wallets]                     # up to 31, no ETH needed in them
amounts    = [w3.to_wei(0.01, "ether")] * len(recipients)
bundle = (w3.to_wei(0.05, "ether"), 0, recipients, amounts, 0, w3.to_wei(0.0005, "ether"), True)   # seed, minSeedOut, ..., minOutEach, gasDrop, useVault
total, bundled, fee = lb.functions.quote(bundle[0], amounts, bundle[5]).call()
tx = lb.functions.launchBundle(params, bundle).build_transaction({"from": me, "value": total, "nonce": w3.eth.get_transaction_count(me), "gas": 3_000_000 + 160_000 * len(recipients)})
h = w3.eth.send_raw_transaction(acct.sign_transaction(tx).raw_transaction); rcpt = w3.eth.wait_for_transaction_receipt(h)
token, curve, vault = lb.functions.launchBundle(params, bundle).call({"from": me, "value": total})   # simulate first: returns (token, curve, vault); no event is emitted
```

### JavaScript (ethers v6)

```js
import { ethers } from "ethers";
const lb = new ethers.Contract("0x35458585776446cd6169F908cc24e3Fa1B44ebFD", ABI, wallet);           // abi.json
const params = ["Name","SYM","ipfs://<image cid>","one line",["https://x.com/…","https://t.me/…","","https://site",""], wallet.address, 0, false, ethers.ZeroHash, ethers.hexlify(ethers.randomBytes(32))];
const recipients = myWallets.map(w => w.address), amounts = recipients.map(() => ethers.parseEther("0.01"));
const bundle = [ethers.parseEther("0.05"), 0n, recipients, amounts, 0n, ethers.parseEther("0.0005"), true];
const [total] = await lb.quote(bundle[0], amounts, bundle[5]);
const rc = await (await lb.launchBundle(params, bundle, { value: total, gasLimit: 3_000_000 + 160_000 * recipients.length })).wait();
const [token, curve, vault] = await lb.launchBundle.staticCall(params, bundle, { value: total });   // simulate first; no event is emitted
```

## FeeVault

Created per launch when `useVault=true`; becomes the token's Pons deployer, so the creator fees (70% of the 1% trade fee, plus any creator tax) are credited to it in the Pons escrow.

```
pending() -> ETH claimable now
harvest() -> (gross, bounty)   // anyone: sweeps curve fees, claims the escrow, pays bounty to msg.sender, protocol split, rest to creator
withdrawCreator()              // creator pulls anything a failed push left behind
rescueToken(erc20)             // any ERC-20 in the vault goes to the creator, nobody else
```

No admin function exists on a vault. Its split is copied at creation and never changes.

## Trust surface

- LaunchBlock keeps no funds; every wei is forwarded, refunded or paid inside `launchBundle`.
- The owner can only change fee parameters, hard-capped in code (service fee ≤ 3%, protocol split ≤ 20%, bounty ≤ 10%), and the treasury address.
- The contract has no path to any launched token, curve or vault balance.

## Receipts

See `RECEIPTS.md` for the deployment transaction, the demo launch (launch + N-wallet bundle in one tx) and the first harvest.


## Receipts summary
Deploy `0xba2e114cc7d0328b743224b79c85a8c7d982b824fb1ce508bf520e4adfc49fa5` · demo launch+bundle `0xddb8d516ee87d7594ab771b6a3e344b15096c3be96cca58f7ac51d67c45d87ba` · first harvest `0xe0c65fb3a4bd563e0e8cacb9531a87112d63e0db3ba4b94f1753a7d57fee04d5` · page https://budapest-concentrate-oriental-mrna.trycloudflare.com
