|
1 | 1 | import * as envalid from 'envalid'; |
| 2 | +import { Awaited } from '@cardano-sdk/util'; |
| 3 | +import { Cardano } from '@cardano-sdk/core'; |
2 | 4 | import { FaucetProvider } from '../../src/FaucetProvider'; |
3 | | -import { SingleAddressWallet } from '@cardano-sdk/wallet'; |
| 5 | +import { ObservableWallet, SingleAddressWallet, StakeKeyStatus } from '@cardano-sdk/wallet'; |
4 | 6 | import { faucetProviderFactory, getLogger, getWallet } from '../../src/factories'; |
5 | 7 | import { filter, firstValueFrom, map } from 'rxjs'; |
6 | 8 |
|
@@ -29,6 +31,55 @@ export const env = envalid.cleanEnv(process.env, { |
29 | 31 |
|
30 | 32 | const logger = getLogger(env.LOGGER_MIN_SEVERITY); |
31 | 33 |
|
| 34 | +const getWalletStateSnapshot = async (wallet: ObservableWallet) => { |
| 35 | + const [rewardAccount] = await firstValueFrom(wallet.delegation.rewardAccounts$); |
| 36 | + const balanceAvailable = await firstValueFrom(wallet.balance.utxo.available$); |
| 37 | + const balanceTotal = await firstValueFrom(wallet.balance.utxo.total$); |
| 38 | + const deposit = await firstValueFrom(wallet.balance.rewardAccounts.deposit$); |
| 39 | + const epoch = await firstValueFrom(wallet.currentEpoch$); |
| 40 | + const utxoTotal = await firstValueFrom(wallet.utxo.total$); |
| 41 | + const utxoAvailable = await firstValueFrom(wallet.utxo.available$); |
| 42 | + return { |
| 43 | + balance: { available: balanceAvailable, deposit, total: balanceTotal }, |
| 44 | + epoch: epoch.epochNo, |
| 45 | + isStakeKeyRegistered: rewardAccount.keyStatus === StakeKeyStatus.Registered, |
| 46 | + rewardAccount, |
| 47 | + utxo: { available: utxoTotal, total: utxoAvailable } |
| 48 | + }; |
| 49 | +}; |
| 50 | +type WalletStateSnapshot = Awaited<ReturnType<typeof getWalletStateSnapshot>>; |
| 51 | + |
| 52 | +const createDelegationCertificates = ( |
| 53 | + { epoch, isStakeKeyRegistered, rewardAccount: { address: rewardAccount } }: WalletStateSnapshot, |
| 54 | + poolId: Cardano.PoolId |
| 55 | +) => { |
| 56 | + const stakeKeyHash = Cardano.Ed25519KeyHash.fromRewardAccount(rewardAccount); |
| 57 | + return [ |
| 58 | + ...(isStakeKeyRegistered |
| 59 | + ? [] |
| 60 | + : ([ |
| 61 | + { |
| 62 | + __typename: Cardano.CertificateType.StakeKeyRegistration, |
| 63 | + stakeKeyHash |
| 64 | + } |
| 65 | + ] as Cardano.Certificate[])), |
| 66 | + { __typename: Cardano.CertificateType.StakeDelegation, epoch, poolId, stakeKeyHash } |
| 67 | + ] as Cardano.Certificate[]; |
| 68 | +}; |
| 69 | + |
| 70 | +const generateTxs = async (wallet1: any, wallet2: any) => { |
| 71 | + const tAdaToSend = 5_000_000n; |
| 72 | + const [{ address: receivingAddress }] = await firstValueFrom(wallet2.wallet.addresses$); |
| 73 | + for (let i = 0; i < 100; i++) { |
| 74 | + const unsignedTxA = await wallet1.wallet.initializeTx({ |
| 75 | + outputs: new Set([{ address: receivingAddress, value: { coins: tAdaToSend } }]) |
| 76 | + }); |
| 77 | + |
| 78 | + const signedTxA = await wallet1.wallet.finalizeTx({ tx: unsignedTxA }); |
| 79 | + await wallet1.wallet.submitTx(signedTxA); |
| 80 | + } |
| 81 | +}; |
| 82 | + |
32 | 83 | describe('Local Network', () => { |
33 | 84 | let faucetProvider: FaucetProvider; |
34 | 85 |
|
@@ -110,4 +161,67 @@ describe('Local Network', () => { |
110 | 161 | wallet1.shutdown(); |
111 | 162 | wallet2.shutdown(); |
112 | 163 | }); |
| 164 | + |
| 165 | + it('will receive rewards for delegated tADA', async () => { |
| 166 | + // Arrange |
| 167 | + const amountFromFaucet = 100_000_000_000; |
| 168 | + |
| 169 | + const wallet1 = await getWallet({ env, logger, name: 'Sending Wallet', polling: { interval: 50 } }); |
| 170 | + const wallet2 = await getWallet({ env, logger, name: 'Receiving Wallet', polling: { interval: 50 } }); |
| 171 | + |
| 172 | + await firstValueFrom(wallet1.wallet.syncStatus.isSettled$.pipe(filter((isSettled) => isSettled))); |
| 173 | + await firstValueFrom(wallet2.wallet.syncStatus.isSettled$.pipe(filter((isSettled) => isSettled))); |
| 174 | + |
| 175 | + const [{ address: sendingAddress }] = await firstValueFrom(wallet1.wallet.addresses$); |
| 176 | + |
| 177 | + const waitForEpoch = async (numberOfEpochsToWait: number) => { |
| 178 | + const currentEpoch = await firstValueFrom(wallet1.wallet.currentEpoch$); |
| 179 | + return await firstValueFrom( |
| 180 | + wallet1.wallet.currentEpoch$.pipe( |
| 181 | + filter(({ epochNo }) => epochNo > currentEpoch.epochNo + numberOfEpochsToWait) |
| 182 | + ) |
| 183 | + ); |
| 184 | + }; |
| 185 | + |
| 186 | + logger.debug(`Address ${sendingAddress.toString()} will be funded with ${amountFromFaucet} tLovelace.`); |
| 187 | + await faucetProvider.request(sendingAddress.toString(), amountFromFaucet, 1); |
| 188 | + await firstValueFrom(wallet1.wallet.balance.utxo.total$.pipe(filter(({ coins }) => coins >= amountFromFaucet))); |
| 189 | + |
| 190 | + const initialState = await getWalletStateSnapshot(wallet1.wallet); |
| 191 | + |
| 192 | + const activePools = await wallet1.providers.stakePoolProvider.queryStakePools({ |
| 193 | + filters: { status: [Cardano.StakePoolStatus.Active] }, |
| 194 | + pagination: { limit: 2, startAt: 0 } |
| 195 | + }); |
| 196 | + const poolId = activePools.pageResults[0].id; |
| 197 | + expect(poolId).toBeDefined(); |
| 198 | + logger.debug(`Wallet funds will be staked to pool ${poolId}.`); |
| 199 | + const certificates = createDelegationCertificates(initialState, poolId); |
| 200 | + |
| 201 | + // Act |
| 202 | + |
| 203 | + // Create stake tx |
| 204 | + const unsignedTx = await wallet1.wallet.initializeTx({ certificates }); |
| 205 | + const signedTx = await wallet1.wallet.finalizeTx({ tx: unsignedTx }); |
| 206 | + await wallet1.wallet.submitTx(signedTx); |
| 207 | + |
| 208 | + let currentEpoch = await firstValueFrom(wallet1.wallet.currentEpoch$); |
| 209 | + logger.debug(`current epoch - ${currentEpoch.epochNo}`); |
| 210 | + currentEpoch = await waitForEpoch(1); |
| 211 | + logger.debug(`waited for epoch - ${currentEpoch.epochNo} to start txs `); |
| 212 | + // workload for generating fees/rewards |
| 213 | + await generateTxs(wallet1, wallet2); |
| 214 | + |
| 215 | + // Assert |
| 216 | + |
| 217 | + currentEpoch = await waitForEpoch(2); |
| 218 | + logger.debug(`waited for epoch - ${currentEpoch.epochNo} to check rewards`); |
| 219 | + |
| 220 | + // wait for rewards |
| 221 | + const reward = await firstValueFrom(wallet1.wallet.balance.rewardAccounts.rewards$.pipe(filter((r) => r > 0))); |
| 222 | + logger.debug(`amount of generated rewards - ${reward}`); |
| 223 | + |
| 224 | + wallet1.wallet.shutdown(); |
| 225 | + wallet2.wallet.shutdown(); |
| 226 | + }); |
113 | 227 | }); |
0 commit comments