From b2b00f3b15fec2df983776603160d3df831dd40e Mon Sep 17 00:00:00 2001 From: farhanqaz Date: Tue, 9 Jun 2026 19:41:50 +0700 Subject: [PATCH 1/2] fix(tests): sync suite with nonce-seeded PDA and update README --- .github/workflows/main.yml | 11 ++++++++ README.md | 45 +++++++++++++++--------------- frontend/README.md | 2 +- tests/nirvana.ts | 57 ++++++++++++++++++++++++++++++++------ 4 files changed, 84 insertions(+), 31 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d64f2de..fcc25fe 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,3 +34,14 @@ jobs: run: | test -f target/deploy/nirvana.so test -f target/idl/nirvana_protocol.json + + - name: Install Node dependencies + run: npm install + + - name: Generate CI wallet + run: | + mkdir -p "$HOME/.config/solana" + solana-keygen new --no-bip39-passphrase -o "$HOME/.config/solana/id.json" --force + + - name: Run integration tests + run: chmod +x run-tests.sh && ./run-tests.sh diff --git a/README.md b/README.md index b963e62..eef71c9 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,8 @@ Before setting up, ensure you have the following installed: Clone the repository and install dependencies: ```bash -git clone https://github.com/soraonchain-byte/Nirvana.git -cd Nirvana +git clone https://github.com/rikokurnia/nirvana-infinity.git +cd nirvana-infinity npm install ``` @@ -32,37 +32,38 @@ npm install To compile the smart contract and generate the IDL: ```bash -anchor build +CARGO_TARGET_DIR=$PWD/target anchor build --ignore-keys ``` -Note: This will generate the `target/` folder containing the program binary (`nirvana.so`) and IDL (`nirvana_protocol.json`). +Note: `--ignore-keys` is required when the local deploy keypair does not match the program ID declared in source (`FxPnV48...`). This generates `target/deploy/nirvana.so` and `target/idl/nirvana_protocol.json`. ### 4. Running Tests -**Option A: Manual (recommended)** +**Option A: Test script (recommended)** ```bash -# Terminal 1: Start local validator -solana-test-validator --reset - -# Terminal 2: Deploy and test -solana config set --url localhost --keypair ~/.config/solana/id.json -solana airdrop 100 -solana program deploy target/deploy/nirvana.so --program-id target/deploy/nirvana-keypair.json -ANCHOR_WALLET=~/.config/solana/id.json \ -ANCHOR_PROVIDER_URL=http://localhost:8899 \ -npx ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts +chmod +x run-tests.sh +./run-tests.sh ``` -**Option B: Anchor test** +The script starts a local validator, loads the program at the declared devnet ID via `--bpf-program`, and runs the full Mocha suite (~2 minutes). -Remove the `[scripts]` section from `Anchor.toml` (if present), then: +**Option B: Manual** ```bash -anchor test +# Terminal 1 +solana-test-validator --reset \ + --bpf-program FxPnV48rg9KkK6huUimjcjL9H4xssM8n7j3uva8k9tmc \ + target/deploy/nirvana.so + +# Terminal 2 +ANCHOR_WALLET=~/.config/solana/id.json \ +ANCHOR_PROVIDER_URL=http://localhost:8899 \ +NODE_OPTIONS="--no-experimental-strip-types" \ +npm test ``` -Note: The test suite validates stream creation, time validation, and token transfers. +Note: The test suite covers stream creation, cliff/milestone vesting, cancel splits, arbiter triggers, and `top_up`. --- @@ -121,9 +122,9 @@ solana program deploy target/deploy/nirvana.so --program-id target/deploy/nirvan ## 🤖 CI/CD Integration This repository is equipped with **GitHub Actions**. Every push or pull request triggers a workflow that: -1. Sets up the Solana/Anchor environment. -2. Runs `anchor build` to check for compilation errors. -3. Executes the test suite to ensure no regressions. +1. Sets up Solana 3.1 + Anchor 1.0. +2. Runs `anchor build --ignore-keys` and verifies build artifacts. +3. Executes `./run-tests.sh` (29 integration tests). --- diff --git a/frontend/README.md b/frontend/README.md index 2d856c7..1ac2d48 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,6 +1,6 @@ # Nirvana Protocol — Frontend -Next.js 15 App Router frontend for the [Nirvana Protocol](https://github.com/soraonchain-byte/Nirvana) equity-streaming smart contract on Solana. +Next.js 15 App Router frontend for the [Nirvana Protocol](https://github.com/rikokurnia/nirvana-infinity) equity-streaming smart contract on Solana. **Live:** https://nirvana-infinity.vercel.app **Program ID (Devnet):** `FxPnV48rg9KkK6huUimjcjL9H4xssM8n7j3uva8k9tmc` diff --git a/tests/nirvana.ts b/tests/nirvana.ts index 791e11c..cd15e14 100644 --- a/tests/nirvana.ts +++ b/tests/nirvana.ts @@ -52,6 +52,13 @@ describe("Nirvana Protocol - Complete Test Suite", () => { // Global payer for mint creation const mintAuthority = anchor.web3.Keypair.generate(); + // Unique nonce per stream (matches frontend/lib/anchor.ts PDA seeds). + let nextNonce = 1; + + function nonceToBuffer(nonce: anchor.BN): Buffer { + return nonce.toArrayLike(Buffer, "le", 8); + } + async function airdrop(pubkey: anchor.web3.PublicKey, amount: number) { const sig = await provider.connection.requestAirdrop(pubkey, amount); const latestBlockHash = await provider.connection.getLatestBlockhash(); @@ -114,10 +121,19 @@ describe("Nirvana Protocol - Complete Test Suite", () => { return { authority, recipient, authorityTokenAccount, recipientTokenAccount }; } - // Helper: derive PDAs - function getPDAs(authority: anchor.web3.PublicKey, recipient: anchor.web3.PublicKey) { + // Helper: derive PDAs (nonce-seeded — must match programs/nirvana/src/lib.rs) + function getPDAs( + authority: anchor.web3.PublicKey, + recipient: anchor.web3.PublicKey, + nonce: anchor.BN + ) { const [statePda] = anchor.web3.PublicKey.findProgramAddressSync( - [Buffer.from("state"), authority.toBuffer(), recipient.toBuffer()], + [ + Buffer.from("state"), + authority.toBuffer(), + recipient.toBuffer(), + nonceToBuffer(nonce), + ], program.programId ); const [vaultPda] = anchor.web3.PublicKey.findProgramAddressSync( @@ -140,9 +156,11 @@ describe("Nirvana Protocol - Complete Test Suite", () => { endTime?: anchor.BN; cliffTime?: anchor.BN; arbiter?: anchor.web3.PublicKey | null; + nonce?: anchor.BN; } ) { const now = Math.floor(Date.now() / 1000); + const nonce = params.nonce ?? new anchor.BN(nextNonce++); const baseAmount = params.baseAmount ?? new anchor.BN(100_000_000); const cliffAmount = params.cliffAmount ?? new anchor.BN(0); const milestoneAmount = params.milestoneAmount ?? new anchor.BN(50_000_000); @@ -151,10 +169,23 @@ describe("Nirvana Protocol - Complete Test Suite", () => { const cliffTime = params.cliffTime ?? new anchor.BN(now + 3); const arbiter = params.arbiter ?? null; - const { statePda, vaultPda } = getPDAs(params.authority.publicKey, params.recipient); + const { statePda, vaultPda } = getPDAs( + params.authority.publicKey, + params.recipient, + nonce + ); await program.methods - .createStream(baseAmount, cliffAmount, milestoneAmount, startTime, endTime, cliffTime, arbiter) + .createStream( + nonce, + baseAmount, + cliffAmount, + milestoneAmount, + startTime, + endTime, + cliffTime, + arbiter + ) .accounts({ authority: params.authority.publicKey, recipient: params.recipient, @@ -168,7 +199,17 @@ describe("Nirvana Protocol - Complete Test Suite", () => { .signers([params.authority]) .rpc(); - return { statePda, vaultPda, baseAmount, cliffAmount, milestoneAmount, startTime, endTime, cliffTime }; + return { + statePda, + vaultPda, + nonce, + baseAmount, + cliffAmount, + milestoneAmount, + startTime, + endTime, + cliffTime, + }; } // ========================================================================= @@ -708,10 +749,10 @@ describe("Nirvana Protocol - Complete Test Suite", () => { const recipientBalance = await getTokenBalance(recipientTokenAccount); const creatorBalanceAfter = await getTokenBalance(authorityTokenAccount); - // Recipient gets linear (~50) + triggered milestone (50) ~= 100. Wide band + // Recipient gets linear (~50) + triggered milestone (50) ~= 100–120. Wide band // absorbs validator clock drift on the linear part. assert.isAbove(recipientBalance, 80); - assert.isBelow(recipientBalance, 120); + assert.isAtMost(recipientBalance, 120); // Creator gets back 150 - recipient (the unvested linear remainder ~50). const creatorGain = creatorBalanceAfter - creatorBalanceBefore; From 5e9f4b8e9cc6e2bf6032c55ceeb2a63b896d2a60 Mon Sep 17 00:00:00 2001 From: farhanqaz Date: Tue, 9 Jun 2026 19:58:45 +0700 Subject: [PATCH 2/2] fix(tests): accept Stream expired message in CI error assertion --- tests/nirvana.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/nirvana.ts b/tests/nirvana.ts index cd15e14..84b9c75 100644 --- a/tests/nirvana.ts +++ b/tests/nirvana.ts @@ -36,6 +36,9 @@ const CANCELLED_ACCOUNT_GONE = // ConstraintSeeds (before, or instead of, the has_one ConstraintHasOne). const UNAUTHORIZED = /ConstraintSeeds|ConstraintHasOne|seeds constraint|has one constraint/i; +// Anchor may surface the error code name or the human-readable #[msg] string. +const STREAM_EXPIRED = /StreamExpired|Stream expired/i; + describe("Nirvana Protocol - Complete Test Suite", () => { const provider = anchor.AnchorProvider.env(); anchor.setProvider(provider); @@ -548,7 +551,7 @@ describe("Nirvana Protocol - Complete Test Suite", () => { assert.fail("Should have thrown StreamExpired"); } catch (err: any) { const t = errText(err); - assert.include(t, "StreamExpired", `expected StreamExpired, got: ${t}`); + assert.match(t, STREAM_EXPIRED, `expected stream expired error, got: ${t}`); } });