Interact with bananapus-721-staking-delegate and bananapus-distributor.
cp .example.env .env # Set up env
npm i # Install dependencies
npm run dev # Dev server
npm run build # Build to /distThis frontend must allow users to connect their wallet, with which they can:
- Stake tokens by calling 
JBERC20PaymentTerminal3_1_2.pay(...)with the appropriate metadata. - Unstake their tokens by calling 
JBERC20PaymentTerminal3_1_2.redeemTokensOf(...)with the appropriate metadata. - The user can register their holdings by calling 
JB721StakingDistributor.claim(...). - Allow users to collect rewards by calling 
JB721StakingDistributor.collect(...). Users should be able to claim past rewards by passing previous_cycles 
721-staking-delegate: Governance NFTs (stake your JBX/NANA/ETC) and vote in onchain (or offchain) governance
Distributor: Distribute rewards to governance users directly (not to their DAO)
Tentacles: Supercharge the above 2 and allow NFT holders to do the same thing on other chains (ex. Optimism, Arbitrum, Polygon, BASE)
Stake by paying a project with an attached banapus-721-staking-delegate. The staking delegate only accepts payments of a defined stakingToken.
_processPayment takes a JBDidPayData:
- The first 32 bytes of metadata are used to pass the referring project's ID, and the next 32 bytes are used for generic extension parameters.
 - Bytes 64-68 (bytes4) are either the 
IJB721StakingDelegateorIJBTiered721Delegate'sinterfaceId. - Next is an ignored bool
 - Then the address of a 
_votingDelegate - Then a 
JB721StakingTier[]of_tierIdsToMint 
Mints these with _mintTiersWithCustomAmount.
redeemParams takes a JBRedeemParamsData:
- The first 32 bytes of metadata are reserved for generic extension parameters.
 - Bytes 32-36 is a 
bytes4interfaceIdwhich must equalIJB721Delegate's. - The remaining metadata is a 
uint256[]of_decodedTokenIds. 
Can either _mintTiers (according to 721 delegate spec) or (with custom staking amount).
_getTierBaseAmount:
| tierId (x) | Return | 
|---|---|
| 1 | 1 | 
| 2-10 | _tierId * 100 - 100 | 
| 11-20 | (_tierId - 10) * 1,000 | 
| 20-30 | (_tierId - 20) * 2,000 + 10,000 | 
| 30-37 | (_tierId - 27) * 10,000 | 
| 37-46 | (_tierId - 36) * 100,000 | 
| 46-55 | (_tierId - 45) * 1,000,000 | 
| 56-58 | (_tierId - 55) * 10,000,000 | 
| 59 | 100,000,000 | 
| 60 | 600,000,000 | 
See chart (including staking amounts) on Notion.
A user calls claim(uint256[] calldata _tokenIds, IERC20[] calldata _tokens) with the tokenIds they hold (and want to claim with) and the tokens that they want to claim. This emits the following event:
event claimed(uint256 indexed tokenId, IERC20 token, uint256 amount, uint256 vestingReleaseBlockNumber);Once the contract is at the vestingReleaseBlockNumber, users can then claim vested rewards with collect:
/**
 * Collect vested tokens
 * @param _tokenIds the nft ids to claim for
 * @param _tokens the tokens to claim
 * @param _cycle the cycle in which the tokens were done vesting
 */
function collect(
    uint256[] calldata _tokenIds,
    IERC20[] calldata _tokens,
    uint256 _cycle
) external { ... }anvil --fork-chain-id 5 --fork-url https://rpc.ankr.com/eth_goerli --fork-block-number <block-number>
forge script DeployGoerli --broadcast --rpc-url http://127.0.0.1:8545 --sender <sender> --private-key <private-key>== Logs ==
  delegate 0x3281688433Be4409A1E64bD604605a57328db416
  distributor 0xf34a740d111f164d7bCf39f8d7B026Fe58b4752E
  terminal 0xdb4CAB6dCAE90f6a0A82D3E37114e2317062Ca41
  token 0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6
  terminalDeployer 0xb0EE6FD8180F3E592E0Ec56c31ee488F42c18de6
  delegateDeployer 0x87d87eA682E66dae0B5a1b4f55DA06C0F5F27682
  staking project ID 1214
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "forge-std/Test.sol";
import "forge-std/console.sol";
import "../src/interfaces/IJB721StakingDelegate.sol";
contract InterfaceIdTest is Test {
  function testLogInterfaceId() public view {
    console.logString("IJB721StakingDelegate Interface ID");
    console.logBytes4(type(IJB721StakingDelegate).interfaceId);
  }
}