From 50a2b2e895ecb99c762222e6ae0a22015f4318dd Mon Sep 17 00:00:00 2001 From: MaziOfWeb3 <142386413+soomtochukwu@users.noreply.github.com> Date: Mon, 9 Jun 2025 15:59:14 +0100 Subject: [PATCH 1/2] Wevaer documentation --- README.md | 389 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 358 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index a016434..dbfe2ce 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,379 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# Weaver Client Documentation -## Getting Started +This guide provides a step-by-step approach to integrating on-chain campaigns into the Weaver platform to enhance user retention, leveraging Starknet's Layer 2 scaling solution. It aligns with the product-driven approach outlined in [Weaver: A Product-Driven Approach to Boosting User Retention with On-Chain Campaigns on Starknet](https://medium.com/@samuonanikesamuel123/weaver-a-product-driven-approach-to-boosting-user-retention-with-on-chain-campaigns-on-starknet-95d5a982b551). The guide includes setup instructions, code examples, and best practices for tracking user interactions and distributing rewards. -First, run the development server: +## Table of Contents +1. [Overview](#overview) +2. [Prerequisites](#prerequisites) +3. [Setting Up the Campaign Smart Contract](#setting-up-the-campaign-smart-contract) +4. [Integrating with Weaver Frontend](#integrating-with-weaver-frontend) +5. [Tracking User Interactions](#tracking-user-interactions) +6. [Distributing Rewards](#distributing-rewards) +7. [Testing and Deployment](#testing-and-deployment) +8. [Best Practices](#best-practices) +9. [Resources](#resources) + +## Overview + +On-chain campaigns on Weaver incentivise user engagement through gamified, blockchain-based interactions on Starknet. These campaigns leverage smart contracts to define rules, track user actions (e.g., transactions, social shares), and distribute rewards (e.g., tokens, NFTs). This guide walks you through creating a campaign where users earn points for completing tasks, which are then redeemable for rewards, ensuring transparency and immutability via Starknet. + +## Prerequisites + +Before starting, ensure you have the following: + +- **Node.js and npm**: Install Node.js (v16 or higher) and npm for managing dependencies. +- **Starknet Development Environment**: Install the [Starknet.js.js](https://www.starknet.io/docs/starknet-js/) library and a Starknet-compatible wallet (e.g., Argent X or Braavos). +- **Cairo Knowledge**: Familiarity with Cairo, Starknet's smart contract language. +- **Weaver Client Setup**: Clone and set up the `weaver-client` repository: + ```bash + git clone https://github.com/weaver-points/weaver-client.git + cd weaver-client + npm install + ``` +- **Starknet Account**: A deployed Starknet account with testnet ETH for gas fees. Use the [Starknet Goerli faucet](https://faucet.goerli.starknet.io/) for testnet funds. +- **Hardhat or Scarb**: Tools for compiling and deploying Cairo contracts. + +## Setting Up the Campaign Smart Contract + +Create a Cairo smart contract to manage campaign logic, including task registration, user participation, and reward distribution. + +1. **Write the Campaign Contract** + +Create a file named `campaign.cairo` in your project’s contracts directory: + +```cairo +%lang starknet +from starkware.cairo.common.cairo_builtins import HashBuiltin +from starkware.cairo.common.uint256 import Uint256 +from starkware.starknet.common.syscalls import get_caller_address + +// Storage for campaign details +@storage_var +func campaign_points(user: felt) -> (points: Uint256) { +} + +// Storage for campaign metadata +@storage_var +func campaign_metadata(campaign_id: felt) -> (active: felt, reward_pool: Uint256) { +} + +// Event emitted when points are awarded +@event +func PointsAwarded(campaign_id: felt, user: felt, points: Uint256) { +} + +// Constructor to initialise campaign +@constructor +func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( + campaign_id: felt, reward_pool: Uint256 +) { + campaign_metadata.write(campaign_id, (1, reward_pool)); + return (); +} + +// Function to award points to a user +@external +func award_points{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( + campaign_id: felt, points: Uint256 +) { + let (caller) = get_caller_address(); + let (current_points) = campaign_points.read(caller); + let new_points = Uint256(current_points.low + points.low, current_points.high + points.high); + campaign_points.write(caller, new_points); + PointsAwarded.emit(campaign_id, caller, points); + return (); +} + +// Function to check if the campaign is active +@view +func is_campaign_active{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( + campaign_id: felt +) -> (active: felt) { + let (metadata) = campaign_metadata.read(campaign_id); + return (metadata.active,); +} +``` + +2. **Compile and Deploy** + +Use Scarb or Hardhat to compile and deploy the contract to Starknet’s Goerli testnet: + +```bash +scarb build +starknet-deploy --network goerli --contract campaign +``` + +Note the deployed contract address for frontend integration. + +## Integrating with Weaver Frontend + +Integrate the campaign contract with the Weaver frontend using Starknet.js to allow users to interact with campaigns. + +1. **Install Starknet.js** + +Ensure `starknet` is installed in your `weaver-client` project: ```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev +npm install starknet ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +2. **Create a Campaign Service** -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +Create a file named `campaignService.ts` in the `src/services` directory: -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +```typescript +import { Contract, Provider, Account, uint256 } from 'starknet'; +import campaignAbi from '../abis/campaign.json'; // ABI from compiled contract -## Learn More +export class CampaignService { + private contract: Contract; + private account: Account; -To learn more about Next.js, take a look at the following resources: + constructor(contractAddress: string, provider: Provider, account: Account) { + this.contract = new Contract(campaignAbi, contractAddress, provider); + this.account = account; + } -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + async awardPoints(campaignId: number, points: number) { + const call = this.contract.populate('award_points', { + campaign_id: campaignId, + points: uint256.bnToUint256(points), + }); + const tx = await this.account.execute(call); + await this.account.waitForTransaction(tx.transaction_hash); + return tx; + } -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + async isCampaignActive(campaignId: number): Promise { + const result = await this.contract.is_campaign_active(campaignId); + return result.active === 1; + } +} +``` + +3. **Connect to Frontend** + +Update `src/App.tsx` to include a campaign interaction component: + +```typescript +import React, { useState } from 'react'; +import { Provider, Account, ec } from 'starknet'; +import { CampaignService } from './services/campaignService'; + +const provider = new Provider({ sequencer: { network: 'goerli-alpha' } }); +const privateKey = 'YOUR_PRIVATE_KEY'; // Replace with secure key management +const starkKeyPair = ec.genKeyPair(); +const account = new Account(provider, 'YOUR_ACCOUNT_ADDRESS', starkKeyPair); +const campaignService = new CampaignService('CONTRACT_ADDRESS', provider, account); -## Deploy on Vercel +const CampaignComponent: React.FC = () => { + const [campaignId, setCampaignId] = useState(1); + const [points, setPoints] = useState(100); + const [status, setStatus] = useState(''); + + const handleAwardPoints = async () => { + try { + await campaignService.awardPoints(campaignId, points); + setStatus('Points awarded successfully!'); + } catch (error) { + setStatus(`Error: ${error.message}`); + } + }; + + return ( +
+

On-Chain Campaign

+ setCampaignId(Number(e.target.value))} + placeholder="Campaign ID" + /> + setPoints(Number(e.target.value))} + placeholder="Points to Award" + /> + +

{status}

+
+ ); +}; + +export default CampaignComponent; +``` -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +Replace `YOUR_PRIVATE_KEY` and `YOUR_ACCOUNT_ADDRESS` with your Starknet account details, and `CONTRACT_ADDRESS` with the deployed contract address. + +## Tracking User Interactions + +Track user interactions (e.g., transactions, social shares) to award points. Use Starknet events to monitor on-chain actions. + +1. **Listen for Events** + +Modify `campaignService.ts` to include event listening: + +```typescript +async listenForPointsAwarded(callback: (campaignId: number, user: string, points: number) => void) { + this.contract.on('PointsAwarded', (campaignId, user, points) => { + callback(Number(campaignId), user.toString(), uint256.uint256ToBN(points).toNumber()); + }); +} +``` + +2. **Integrate in Frontend** + +Update `CampaignComponent` to display awarded points: + +```typescript +import React, { useEffect, useState } from 'react'; +// ... other imports + +const CampaignComponent: React.FC = () => { + const [campaignId, setCampaignId] = useState(1); + const [points, setPoints] = useState(100); + const [status, setStatus] = useState(''); + const [events, setEvents] = useState([]); + + useEffect(() => { + campaignService.listenForPointsAwarded((campaignId, user, points) => { + setEvents((prev) => [...prev, `Campaign ${campaignId}: User ${user} awarded ${points} points`]); + }); + }, []); + + // ... rest of the component + return ( +
+

On-Chain Campaign

+ setCampaignId(Number(e.target.value))} + placeholder="Campaign ID" + /> + setPoints(Number(e.target.value))} + placeholder="Points to Award" + /> + +

{status}

+

Recent Awards

+
    + {events.map((event, index) => ( +
  • {event}
  • + ))} +
+
+ ); +}; +``` + +## Distributing Rewards + +Distribute rewards (e.g., tokens or NFTs) based on accumulated points. + +1. **Add Reward Distribution to Contract** + +Update `campaign.cairo` to include a reward distribution function: + +```cairo +// Function to distribute rewards +@external +func distribute_rewards{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( + campaign_id: felt, user: felt, amount: Uint256 +) { + let (metadata) = campaign_metadata.read(campaign_id); + assert metadata.active = 1; // Ensure campaign is active + let (current_points) = campaign_points.read(user); + assert current_points.low >= amount.low; // Ensure user has enough points + let new_points = Uint256(current_points.low - amount.low, current_points.high - amount.high); + campaign_points.write(user, new_points); + // Logic to transfer tokens/NFTs (requires integration with a token contract) + return (); +} +``` + +2. **Integrate Reward Distribution** + +Add a method to `campaignService.ts`: + +```typescript +async distributeRewards(campaignId: number, user: string, amount: number) { + const call = this.contract.populate('distribute_rewards', { + campaign_id: campaignId, + user, + amount: uint256.bnToUint256(amount), + }); + const tx = await this.account.execute(call); + await this.account.waitForTransaction(tx.transaction_hash); + return tx; +} +``` + +3. **Update Frontend** + +Add a reward distribution button to `CampaignComponent`: + +```typescript +// ... inside CampaignComponent +const [userAddress, setUserAddress] = useState(''); +const [rewardAmount, setRewardAmount] = useState(50); + +const handleDistributeRewards = async () => { + try { + await campaignService.distributeRewards(campaignId, userAddress, rewardAmount); + setStatus('Rewards distributed successfully!'); + } catch (error) { + setStatus(`Error: ${error.message}`); + } +}; + +// ... inside return + setUserAddress(e.target.value)} + placeholder="User Address" +/> + setRewardAmount(Number(e.target.value))} + placeholder="Reward Amount" +/> + +``` + +## Testing and Deployment + +1. **Test Locally** + +Run the Weaver client locally: + +```bash +npm run start +``` -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +Test the campaign functionality using a Starknet wallet connected to the Goerli testnet. +2. **Deploy to Production** -figma design Desktop View: [Weaver Desktop View](https://www.figma.com/design/8myxec6w5hwleSPA7v7u1X/Weaver---PC-UI?node-id=2-2&t=OxY8h7UZmRjHH9VG-1) +Deploy the contract to Starknet mainnet and update `CONTRACT_ADDRESS` in `campaignService.ts`. Ensure secure key management for the account’s private key. -Figma desktop onboarding view design: [Weaver onboarding flow](https://www.figma.com/design/wEV47dLGdhsu8GbfkpeAVc/Weaver---Onboarding?m=auto&t=FoM7vbyzwjZT62ZE-6) +## Best Practices -Updated figma design Desktop view: [Weaver updated desktop view](https://www.figma.com/design/w6f7o1GTsgB3rJqSBPg9b5/Weaver---PC-UI-(Updated-UI)?node-id=2-2&t=5ljIMtnRp3hjUuaT-1) -Rebranded figma design for weaver: [Weaver rebrand](https://www.figma.com/design/JO1BCPiKkbtQnRpChoCp0v/Weaver--New-?node-id=0-1&t=bKpJvUYaOoQNzQCG-1) +- **Security**: Use secure key management (e.g., environment variables or a vault) for private keys. Validate all inputs to prevent reentrancy or overflow attacks. +- **Gas Optimisation**: Minimise storage operations in the Cairo contract to reduce gas costs on Starknet. +- **User Experience**: Provide clear feedback in the UI for transaction status and errors. Use loading indicators during transaction confirmations. +- **Testing**: Write unit tests for the contract using Cairo’s testing framework and integration tests for the frontend using Jest. +- **Monitoring**: Use Starknet’s event system to monitor campaign activity and integrate with analytics tools for off-chain tracking. -## Continuous Integration +## Resources -We use GitHub Actions to ensure code quality: -- Automatic build validation on every push and PR -- Linting checks -- Test execution -- Build verification +- [Starknet Documentation](https://www.starknet.io/docs/) +- [Starknet.js](https://www.starknet.io/docs/starknet-js/) +- [Weaver Client Repository](https://github.com/weaver-points/weaver-client) +- [Medium Article on Weaver Campaigns](https://medium.com/@samuonanikesamuel123/weaver-a-product-driven-approach-to-boosting-user-retention-with-on-chain-campaigns-on-starknet-95d5a982b551) From db83cc8a329dd2efad2ed8e635a0a42e565adeb8 Mon Sep 17 00:00:00 2001 From: MaziOfWeb3 <142386413+soomtochukwu@users.noreply.github.com> Date: Mon, 9 Jun 2025 15:59:48 +0100 Subject: [PATCH 2/2] Weaver Client Documentation --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dbfe2ce..ad8ea59 100644 --- a/README.md +++ b/README.md @@ -377,3 +377,4 @@ Deploy the contract to Starknet mainnet and update `CONTRACT_ADDRESS` in `campai - [Starknet.js](https://www.starknet.io/docs/starknet-js/) - [Weaver Client Repository](https://github.com/weaver-points/weaver-client) - [Medium Article on Weaver Campaigns](https://medium.com/@samuonanikesamuel123/weaver-a-product-driven-approach-to-boosting-user-retention-with-on-chain-campaigns-on-starknet-95d5a982b551) +-