Staking Pools (Pule z oprocentowanymi tokenami)
Przegląd
BiatecCLAMM teraz obsługuje pule stakingowe, gdzie aktywo A i aktywo B to ten sam token. Umożliwia to utworzenie oprocentowanych tokenów takich jak B-ALGO, B-USDC itp., gdzie dostawcy płynności mogą otrzymywać nagrody ze stakingu, opłat transakcyjnych lub innych źródeł dochodów, które gromadzą się w puli.
Przypadki użycia
1. Native Token Staking (B-ALGO)
Utwórz pulę, gdzie aktywo A i aktywo B są ustawione na 0 (natywny token). To utworzy token B-ALGO reprezentujący stakowane ALGO. Jakiekolwiek ALGO, które zgromadzi się w puli (np. z nagród konsensusowych, nagród governance lub bezpośrednich depozytów), może być dystrybuowane posiadaczom B-ALGO.
2. Asset Staking (B-USDC, B-TOKEN, itp.)
Utwórz pulę, gdzie aktywo A i aktywo B są ustawione na ten sam ASA ID. To utworzy token B-{TOKEN} reprezentujący stakowane tokeny. Jest to przydatne dla:
- Protokołów pożyczkowych, gdzie zdeponowane aktywa zarabiają odsetki
- Mechanizmów dzielenia się dochodami
- Strategii agregacji dochodów
Charakterystyki puli
Kiedy aktywo A równa się aktywowi B:
- Nazwa tokenu LP:
B-{AssetName}(np. "B-ALGO", "B-USDC") - Symbol tokenu LP: Nazwa jednostki aktywa (np. "ALGO", "USDC")
- Zakres cenowy: Musi być płaski na 1:1 (priceMin = priceMax = currentPrice = SCALE). Kontrakt teraz wymusza to podczas bootstrap (błąd E_STAKING_PRICE).
- Swapy: Nie mają sensu, ponieważ oba aktywa są identyczne
- Główne operacje: Dodać płynność (stake), usunąć płynność (unstake), dystrybuować nagrody
Jak to działa
1. Tworzenie puli
const { clientBiatecClammPoolProvider } = await setupPool({
algod,
assetA: 0n, // 0 dla ALGO, lub ASA ID
assetB: 0n, // To samo co assetA
biatecFee: 0n, // Brak opłaty Biatec
lpFee: BigInt(SCALE / 100), // 1% opłata (opcjonalne)
p: BigInt(1 * SCALE), // Cena = 1:1
p1: BigInt(1 * SCALE), // Min cena = 1
p2: BigInt(1 * SCALE), // Max cena = 1
nativeTokenName: 'Algo', // Opcjonalny parametr pomocniczy zapewnia zgodność globalnego stanu providera
});
// Przy ręcznym konstruowaniu transakcji, skonfiguruj provider raz przez:
// await poolProviderClient.send.setNativeTokenName({
// args: { appBiatecConfigProvider: configAppId, nativeTokenName: 'Algo' },
// appReferences: [configAppId],
});
2. Dodawanie płynności (Staking)
Użytkownicy dodają płynność do stakingu swoich tokenów:
const txId = await clammAddLiquiditySender({
algod,
account: userSigner,
amountA: stakeAmount, // Kwota do stakingu
amountB: stakeAmount, // To samo co amountA
assetA: 0n, // 0 dla ALGO
assetB: 0n, // To samo co assetA
assetLP: lpTokenId,
clientBiatecClammPool,
appBiatecConfigProvider,
appBiatecIdentityProvider,
appBiatecPoolProvider,
});
Użytkownicy otrzymują tokeny LP B-{TOKEN} reprezentujące ich pozycję stakingową.
3. Dystrybucja nagród
Kiedy nagrody zgromadzą się w puli (np. nagrody stakingowe, opłaty, bezpośrednie depozyty), adres opłaty wykonawczej je dystrybuuje:
// 1. Nagrody są deponowane na adres puli
// (To może nastąpić automatycznie przez nagrody konsensusowe, lub r ęcznie)
// 2. Adres opłaty wykonawczej dystrybuuje nadwyżkowe aktywa
const txId = await clammDistributeExcessAssetsSender({
algod,
account: executiveSigner,
amountA: rewardsAmount * (SCALE / assetDecimals), // W skali podstawowej (9 miejsc dziesiętnych)
amountB: 0n, // Brak nagród dla aktywa B
assetA: 0n, // 0 dla ALGO
assetB: 0n, // To samo co assetA
clientBiatecClammPool,
appBiatecConfigProvider,
});
To zwiększa płynność puli proporcjonalnie, więc kiedy użytkownicy wypłacają, otrzymują więcej tokenów niż wpłacili.
4. Usuwanie płynności (Unstaking)
Użytkownicy wypłacają swój stake plus zarobione nagrody:
const txId = await clammRemoveLiquiditySender({
algod,
account: userSigner,
assetA: 0n,
assetB: 0n,
assetLP: lpTokenId,
lpTokensToSend: lpBalance, // Wszystkie lub częściowe tokeny LP
clientBiatecClammPool,
appBiatecConfigProvider,
appBiatecIdentityProvider,
appBiatecPoolProvider,
});
Użytkownicy otrzymują z powrotem swoje stakowane tokeny plus proporcjonalny udział w nagrodach.
Szczegóły techniczne
Zmiany w kontrakcie
-
Stan globalny pool providera: Dodany
nativeTokenName(nt) do stanu globalnego pool providera z metodą admin-onlysetNativeTokenName. CLAMM bootstrap czyta tę wartość podczas tworzenia tokenów LP. -
Walidacja aktywów: Usunięte twierdzenie, które uniemożliwiało
assetA.id === assetB.id. Kontrakt teraz obsługuje tę konfigurację dla pul stakingowych. -
LP Token naming:
- Pule standardowe:
B-{AssetA}-{AssetB}z nazwą jednostkiBLP - Pule stakingowe:
B-{AssetName}z nazwą jednostki odpowiadającą podstawowemu aktywowi
- Pule standardowe:
-
Logika opt-in: Pomija duplikowane opt-in kiedy aktywo A równa się aktywowi B.
Zmiany API TypeScript
-
clammCreateTxs / clammCreateSender: Nie przyjmują już parametru
nativeTokenName; skonfiguruj pool provider raz przezBiatecPoolProviderClient.send.setNativeTokenName. -
setupPool: Dodane opcjonalne parametry
assetBinativeTokenNamedla scenariuszy testowych
Przykłady
Przykład 1: Pula B-ALGO
// Utwórz pulę B-ALGO
const pool = await setupPool({
algod,
assetA: 0n,
assetB: 0n,
biatecFee: 0n,
lpFee: 0n,
p: BigInt(SCALE),
p1: BigInt(SCALE),
p2: BigInt(SCALE),
nativeTokenName: 'Algo',
});
// Stakuj 100 ALGO
await clammAddLiquiditySender({
amountA: 100n * BigInt(SCALE_ALGO),
amountB: 100n * BigInt(SCALE_ALGO),
// ... pozostałe parametry
});
// Symuluj 10 ALGO nagród zgromadzonych w puli
// (Wysłane bezpośrednio na adres puli)
// Dystrybuuj nagrody
await clammDistributeExcessAssetsSender({
amountA: 10n * BigInt(SCALE), // 10 ALGO w skali podstawowej
amountB: 0n,
// ... pozostałe parametry
});
// Unstakuj (otrzymuje oryginalne 100 ALGO + proporcjonalne nagrody)
await clammRemoveLiquiditySender({
lpTokensToSend: lpBalance,
// ... pozostałe parametry
});
Przykład 2: Pula B-USDC (Protokół pożyczkowy)
// Utwórz pulę B-USDC
const pool = await setupPool({
algod,
assetA: USDC_ASSET_ID,
assetB: USDC_ASSET_ID,
biatecFee: 0n,
lpFee: 0n,
p: BigInt(SCALE),
p1: BigInt(SCALE),
p2: BigInt(SCALE),
});
// Użytkownik wpłaca 1000 USDC
await clammAddLiquiditySender({
amountA: 1000n * BigInt(USDC_DECIMALS),
amountB: 1000n * BigInt(USDC_DECIMALS),
// ... pozostałe parametry
});
// Stopniowo gromadzą się odsetki pożyczkowe w puli
// Adres wykonawczy dystrybuuje odsetki posiadaczom LP
await clammDistributeExcessAssetsSender({
amountA: interestAmount,
amountB: 0n,
// ... pozostałe parametry
});
// Użytkownik wypłaca swój depozyt + zarobione odsetki
await clammRemoveLiquiditySender({
lpTokensToSend: lpBalance,
// ... pozostałe parametry
});
Uwagi bezpieczeństwa
Model zaufania
Pule stakingowe (B-ALGO, B-USDC, itp.) wymagają zaufania do specyficznych adresów i procesów:
-
Kontrola adresu opłaty wykonawczej: Tylko konto
addressExecutiveFeeskonfigurowane w Biatec Config Provider może dystrybuować nagrody przezdistributeExcessAssets. Ten adres ma znaczną władzę:- Może dystrybuować nagrody wszystkim posiadaczom LP
- Może wpływać na czasowanie dystrybucji nagród
- Musi dokładnie kalkulować ilości nagród
- Zalecenie: Użyj konta multi-signature dla tego adresu
-
Integralność config providera: Kontrakt config provider kontroluje krytyczne parametry:
- Referencja identity providera
- Struktura opłat
- Adresy wykonawcze
- Zalecenie: Zapewnij, aby config provider był immutable lub kontrolowany przez DAO
Różnice od pul płynności
Pule stakingowe mają unikalne charakterystyki:
- Brak impermanent loss: Ponieważ oba aktywa są identyczne, nie ma ryzyka cenowego
- Brak swap price discovery: Nagrody pochodzą z zewnętrznych źródeł, nie z transakcji
- Prostszy model cenowy: Zawsze 1:1 w skali podstawowej
- Stawka nagród zewnętrznie ustawiona: Dochody zależą od dystrybucji nagród przez adres wykonawczy
- Operacje swap zablokowane: Kontrakt jawnie blokuje swapy z błędem "Swaps not allowed in staking pools"
Czynniki ryzyka
-
Brak nagród: Jeśli nagrody nie są dystrybuowane zgodnie z oczekiwaniami, staking nie przynosi korzyści
- Łagodzenie: Monitoruj aktywność adresu wykonawczego i plan dystrybucji nagród
-
Błędy księgowe: Nieprawidłowe wywołania
distributeExcessAssetsmogą zablokować środki lub dystrybuować niesprawiedliwie- Łagodzenie: Dokładnie testuj kalkulacje dystrybucji nagród off-chain najpierw
- Łagodzenie: Użyj skali podstawowej (9 miejsc dziesiętnych) dla wszystkich kalkulacji
-
Zmiany governance: Zmiana adresu opłaty wykonawczej wpływa na kontrolę
- Łagodzenie: Wymagaj timelock dla zmian adresów
- Łagodzenie: Multi-sig governance dla aktualizacji config
-
Walidacja cenowa: Pule stakingowe wymagają
priceMin === priceMax === currentPrice- Kontrakt teraz wymusza tę walidację w funkcji
bootstrap - Kod błędu:
E_STAKING_PRICEjeśli zakres cenowy nie jest płaski
- Kontrakt teraz wymusza tę walidację w funkcji
-
Walidacja kolejności aktywów: Pule standardowe teraz wymuszają
assetA.id < assetB.iddla pul non-staking- Pule stakingowe omijają tę kontrolę kiedy
assetA.id === assetB.id - Kod błędu:
E_ASSET_ORDERjeśli kolejność jest nieprawidłowa w pulach standardowych
- Pule stakingowe omijają tę kontrolę kiedy
Sprawdzone praktyki dystrybucji nagród
- Kalkuluj w skali podstawowej: Zawsze konwertuj ilości nagród do skali podstawowej (pomnóż przez 1e9) przed wywołaniem
distributeExcessAssets - Zweryfikuj bilans puli: Zapewnij, że pula rzeczywiście otrzymała tokeny nagród przed dystrybucją
- Testuj najpierw: Uruchom dystrybucję nagród na testnecie z małymi ilościami najpierw
- Dokumentuj plan: Jasno komunikuj częstotliwość i ilości dystrybucji nagród
- Monitoruj stan: Śledź płynność puli przed i po dystrybucji w celu weryfikacji poprawności
Przykład bezpiecznej dystrybucji nagród
// 1. Kalkuluj nagrodę w skali podstawowej
const rewardInTokenUnits = 1000n; // 1000 tokenów
const tokenDecimals = 6n; // USDC ma 6 miejsc dziesiętnych
const baseScale = 1_000_000_000n;
const rewardInBaseScale = rewardInTokenUnits * (baseScale / 10n ** tokenDecimals);
// 2. Najpierw wyślij tokeny do puli
await algod.sendPaymentTransaction({
from: executiveAddress,
to: poolAddress,
amount: rewardInTokenUnits * 10n ** tokenDecimals, // W jednostkach natywnych
});
// 3. Dystrybuuj przez kontrakt
await clammDistributeExcessAssetsSender({
appBiatecClammPool: poolAppId,
appBiatecConfigProvider: configAppId,
assetA: usdcAssetId,
assetB: usdcAssetId,
amountA: rewardInBaseScale,
amountB: 0n,
// ... pozostałe parametry
});
Audyty bezpieczeństwa
Wykonano wiele audytów bezpieczeństwa opartych na AI dla implementacji pul stakingowych. Kluczowe ustalenia rozwiązane:
- [M-02] Dodano walidację dla zakresów cenowych pul same-asset (muszą być płaskie)
- [M-06] Zalecane dodanie dowodu depozytu do
distributeExcessAssetsdla bezpieczeństwa - Ogólne Pule stakingowe są teraz jawnie walidowane podczas bootstrap
Zobacz folder audits/ dla szczegółowych raportów audytów bezpieczeństwa.
Ochrona użytkowników
-
Kalkulacja nagród: Metoda
distributeExcessAssetszwiększa płynność puli proporcjonalnie. Zapewnij, że parametramountAjest kalkulowany poprawnie w skali podstawowej (9 miejsc dziesiętnych). -
Stabilność cenowa: Ponieważ pule stakingowe używają stosunku cenowego 1:1, cena powinna pozostać stała. Jakakolwiek znacząca odchylka może wskazywać na problem.
-
Zaokrąglanie: Podobnie jak w przypadku pul standardowych, małe ilości mogą być utracone przy zaokrąglaniu. Jest to zamierzone w celu ochrony puli przed krwawieniem.
Testowanie
Zobacz __test__/pool/staking.test.ts dla kompleksowych przykładów testów włączając:
- Tworzenie puli B-ALGO
- Tworzenie puli B-TOKEN z ASA
- Dystrybucja nagród i weryfikacja zysku LP
Konfiguracja specyficzna dla łańcucha
Różne sieci blockchain mogą używać różnych nazw tokenów natywnych:
- Algorand Mainnet/Testnet: 'ALGO'
- Voi Network: 'VOI'
- Aramid Network: 'ARAMID'
Użyj setNativeTokenName do skonfigurowania providera przed deployowaniem pul, aby zapewnić, że nazewnictwo tokenów LP odpowiada docelowej sieci.