Vaults
Một tax token thu một slice của mỗi lệnh mua và bán. Slice đó đi tới một fee receiver. Trong đợt launch tax-token thông thường, fee receiver là một ví. Trong một launch dùng vault, fee receiver là một contract được deploy tại thời điểm launch, nên doanh thu đến một nơi có luật lệ: chia nó cho nhiều ví, stream nó tới một người nhận, giữ nó cho một danh tính, hoặc bất cứ điều gì khác mà tác giả vault code ra.
Trang này dành cho các bên tích hợp muốn launch với một vault hoặc đọc một vault. Nếu bạn muốn viết vault của riêng mình, xem custom-vault.md.
Mục lục
- Cách doanh thu đến một vault
- Launch với một vault
- Doanh thu native vs ERC20
- Các vault CheesePad cung cấp
- Đọc một vault một cách tổng quát
- Địa chỉ factory
Cách doanh thu đến một vault
trader ──buy/sell──▶ tax token
│ collects tax on the transfer
▼
settles the tax ← permissionless; anyone can poke it
│ splits by the allocation you set at launch
┌──────────────┼───────────────┬───────────────┐
▼ ▼ ▼ ▼
fee receiver holders burn liquidity
(the vault) (dividends) (0xdead) (adds LP)
Token không đẩy doanh thu ở mỗi giao dịch. Nó tích lũy thuế và settle theo từng đợt. Khi settle,
slice fee receiver được gửi tới vault. Vault của bạn ghi nhận doanh thu và trả nó ra sau, trên các
lệnh gọi distribute() / claim() của chính nó. Việc gom theo đợt này là lý do một vault luôn có
hình dạng "tích lũy ngay, trả sau."
Khoản phí platform 10% của CheesePad trên creator tax là một khoản phí tách biệt, không phải một phần của allocation của bạn; xem mô hình phí token để biết nó phù hợp thế nào bên cạnh thuế.
Bốn slice allocation đến từ allocation bạn đặt trong taxConfig lúc launch, và chúng phải cộng lại
bằng 10000 BPS:
| Slice | Nó đi đâu |
|---|---|
feeReceiver | vault |
holders | token holder, dưới dạng dividend (cần một minHolderShare khác không) |
burn | địa chỉ dead |
liquidity | được thêm lại làm LP |
Để một vault từng được trả, allocation.feeReceiver phải lớn hơn không và ít nhất một bên thuế phải
khác không. Launchpad kiểm tra điều này và revert launch nếu không.
Launch với một vault
Dùng createMicroSaleTaxTokenWithVaultV2. Trên CreateParamsV2 thông thường, bạn truyền vào một địa
chỉ vault factory, một blob vaultData đã được ABI-encode để cấu hình vault cụ thể, và dexKey đặt
tên DEX để list.
function createMicroSaleTaxTokenWithVaultV2(
CreateParamsV2 calldata params, // same struct as the tax-token launch, incl. taxConfig
address vaultFactory, // a factory the platform trusts (see Factory addresses)
bytes calldata vaultData, // ABI-encoded config for this vault; shape depends on factory
address dexKey // the DEX to list on
) external payable returns (address fundManager);
Launchpad làm gì, theo thứ tự, tất cả fail-closed (bất kỳ thất bại nào cũng revert toàn bộ launch):
- Xác nhận factory trỏ tới launchpad này.
- Xác nhận factory chấp nhận quote token của sale.
- Xác nhận factory hiểu revision launch-data hiện tại.
- Gọi review hook của factory với các tham số launch của bạn; nó phải approve.
- Cho factory deploy vault.
- Xác nhận revenue mode của vault khớp với quote token (xem bên dưới).
- Gắn vault làm fee receiver của token.
Bạn không bao giờ tự đấu nối gì cả. Nếu factory approve, bạn có một token đang chạy với thuế chảy vào một vault mới.
Cùng handshake đó hậu thuẫn một bonding curve tax token: createBondingCurveTaxTokenWithVaultV2 nhận
một vaultFactory, vaultData, và dexKey và chạy các kiểm tra giống hệt. Ràng buộc là revenue mode
của vault phải khớp với đồng tiền quote của curve — native mode cho một curve BNB, ERC20 mode cho một
curve được định giá bằng một quote token ERC20 (xem Doanh thu native vs ERC20).
Mọi thứ bên dưới áp dụng cho cả hai kiểu launch.
Encode vaultData
vaultData là opaque đối với launchpad; mỗi factory tự định nghĩa hình dạng của nó. Một factory công
bố hình dạng đó qua vaultDataSchema(), nên một UI có thể build form một cách tổng quát. Để encode
bằng tay, hãy theo thứ tự trường và kiểu của schema. Các ví dụ cụ thể cho hai vault được cung cấp nằm
bên dưới.
Lấy lại địa chỉ vault
Sau khi launch, đọc nó từ sale:
const sale = await launchpad.getMicroSaleByFundManager(fundManager);
console.log("vault:", sale.vault); // the deployed vault
console.log("via factory:", sale.vaultFactory); // the factory that produced it
Sale cũng ghi lại factory nào đã tạo ra vault, nên một allowlist off-chain có thể gắn nhãn cho các vault đến từ một factory đáng tin cậy.
Doanh thu native vs ERC20
Một vault xử lý doanh thu bằng đúng một tài sản, được quyết định lúc launch:
- Native mode khi quote token của sale là WBNB. Doanh thu đến dưới dạng BNB.
- ERC20 mode trong các trường hợp khác. Doanh thu đến dưới dạng quote token, và các đồng coin native lạc bị từ chối.
Launchpad khẳng định rằng mode của vault khớp với quote token, nên một factory không khớp không thể brick việc giao dịch. Với một launch được định giá bằng ERC20, contributor raise bằng chính quote token ERC20 đó — xem microsale.md. Quote token đến từ ràng buộc của template, nên một launch fee-receiver ví hoặc không thuế dựa trên một template gắn với một quote token ERC20 cũng raise bằng chính quote token đó; điểm khác biệt với một vault chỉ là nơi slice fee-receiver đến.
Các vault CheesePad cung cấp
Hai vault làm sẵn, mỗi vault có factory riêng. Bạn trỏ createMicroSaleTaxTokenWithVaultV2 tới factory
và truyền vaultData tương ứng.
SplitterVault: các payee cố định theo tỷ lệ
Chia doanh thu cho một danh sách ví cố định theo basis point. Tối đa 50 payee, các share phải cộng lại bằng 10000, không trùng lặp, không địa chỉ không.
vaultData là abi.encode(address[] payees, uint16[] bps):
const payees = ["0xAaa...", "0xBbb..."];
const bps = [7000, 3000]; // 70% / 30%; must sum to 10000
const vaultData = ethers.AbiCoder.defaultAbiCoder().encode(
["address[]", "uint16[]"],
[payees, bps]
);
await launchpadRW.createMicroSaleTaxTokenWithVaultV2(params, SPLITTER_FACTORY, vaultData, dexKey, {
value: creationFee,
});
Dùng vault sau khi launch:
distribute()là permissionless. Nó ghi có share của mỗi payee từ số dư đã tích lũy và đẩy nó tới họ. Nếu một payee không thể nhận (một contract bị revert, chẳng hạn), share của họ được giữ lại làmowedđể họ pull sau, và những người khác vẫn được trả.claim()cho phép một payee pull số dưowedcủa chính mình.splitConfig()trả về các payee và bps của họ;owed(address)trả về số dư đang chờ của một payee;credited()trả về tổng cộng đang chạy đã được ghi có.
SplitterVault chấp nhận bất kỳ quote token nào.
GiftVault: stream tới ví đứng sau một X handle
Giữ doanh thu của một token cho người đứng sau một X (Twitter) handle. Không ai được trả cho tới khi một ví được bind với handle, và việc bind được ủy quyền bởi một chữ ký relayer của platform, nên chỉ chủ thực sự của handle mới có thể claim.
vaultData là abi.encode(string xHandle, uint256 timeoutPeriod):
const vaultData = ethers.AbiCoder.defaultAbiCoder().encode(
["string", "uint256"],
["cheesepad", 30n * 24n * 60n * 60n] // handle, and 30-day bind window
);
await launchpadRW.createMicroSaleTaxTokenWithVaultV2(params, GIFT_FACTORY, vaultData, dexKey, {
value: creationFee,
});
timeoutPeriod nằm giữa 1 và 365 ngày. Factory của GiftVault cũng yêu cầu allocation.holders lớn
hơn không, vì cơ chế fallback bên dưới.
Nó diễn ra thế nào:
- Bind. Chủ ví lấy một bằng chứng được relayer ký (một chữ ký EIP-712 trên handle, token, ví
beneficiary, một nonce, và một expiry) và gọi
bindBeneficiary(beneficiary, nonce, validUntil, signature). Từ đó trở đi, doanh thu stream tới ví đó. Việc bind chỉ mở cho tới timeout, và chỉ một khi bằng chứng được kiểm chứng. - Withdraw. Sau khi bind,
distribute()đẩy doanh thu đã tích lũy tới beneficiary, vàclaim()cho phép họ pull nó. - Lock. Beneficiary có thể gọi
lock()để cắt relayer và platform ra khỏi bất kỳ lần rebind nào trong tương lai. - Rescue. Trước timeout và trước một lock, backstop của platform có thể sửa một ví bị bind sai. Nó chỉ định tuyến lại doanh thu trong tương lai.
- Dividend fallback. Nếu không ai bind trước timeout, bất kỳ ai cũng có thể gọi
distributeDividend(), thứ định tuyến doanh thu chưa được claim tới các holder của token dưới dạng dividend. Đây là lý do factory nhất quyết đòi một slice holder. Một khi nó kích hoạt, vault vĩnh viễn ở chế độ dividend và việc bind bị đóng.
Đọc một vault một cách tổng quát
Mỗi vault mô tả các trường đọc và các hành động của chính nó qua vaultUISchema(). Một UI có thể
render một vault nó chưa từng thấy: nó đọc schema, gọi từng phương thức đọc, và giải mã kết quả bằng
các kiểu đã khai báo. Hình dạng chung:
const vault = new ethers.Contract(sale.vault, VaultABI, provider);
const isNative = await vault.isNativeRevenue(); // true = BNB, false = ERC20
const quote = await vault.quoteToken(); // revenue asset
const desc = await vault.description(); // one-liner for listings
const schema = await vault.vaultUISchema(); // methods to render (reads + writes)
vaultUISchema() trả về một vaultType, một description, và một danh sách phương thức. Mỗi phương
thức hoặc là một trường đọc (UI gọi nó và hiển thị kết quả có kiểu) hoặc một hành động ghi (UI render
một nút và một form nhập, cộng với bất kỳ ERC20 approval nào nó cần trước). Các kiểu schema được nói
tới trong custom-vault.md và
events-and-data.md.
Phần quan trọng cho một reader tổng quát: một vault chỉ phơi ra những gì schema của nó khai báo. Không có introspection storage tự động, nên hãy tin schema như danh sách của mọi thứ đáng hiển thị.
Địa chỉ factory
| Vault | Network | Factory |
|---|---|---|
| SplitterVault | BNB (56) | 0x9d04B0Bfe281411d4A01a69BD89f7Ba6b7F5A59b |
| SplitterVault | BNB testnet (97) | 0xbB5165684832a8Cda1121081EBb168ca6636abc6 |
| SplitterVault | Robinhood (4663) | 0x58DBab8C3529492c12Ee913313Cf600f522a39D9 |
| GiftVault | BNB (56) | 0xFa36D3e2240BEDB22bad35D8FD4165AAf0598896 |
| GiftVault | BNB testnet (97) | 0x8227F4330E3aa43c94b9fDc409D71A6C6C2033B7 |
| GiftVault | Robinhood (4663) | 0x7FD34AeA57250aC66C59448339316aF745f6B9ef |
Một factory là stateless và immutable. Một phiên bản mới là một địa chỉ mới, nên hãy xác nhận địa chỉ hiện tại với đội ngũ CheesePad trước khi bạn tự động hóa các launch dựa trên nó.