Ctory is a decentralized platform that revolutionizes storytelling through blockchain technology. It enables creators to publish and monetize content with full ownership while readers can discover immersive stories in a web3-powered ecosystem.
To create a global, censorship-resistant platform for storytellers that provides equitable compensation, ownership, and discovery through decentralized technologies.
- True Ownership: All content is stored on decentralized storage with creator-owned NFTs
- AI-Enhanced Creation: Integrated AI tools to assist storytellers
- Cross-chain Compatibility: Support for multiple blockchain networks
- Community Governance: Platform decisions driven by token holders
- Architecture
- Technology Stack
- Project Structure
- Core Features
- Data Flow
- Getting Started
- Development
- Security
- Performance Optimization
- Testing Strategy
- Deployment
- Roadmap
- FAQ
- Contributing
- Acknowledgements
- License
graph TD
A[Web Client] --> B[React Components]
B --> C[Redux Store]
C --> D[Services Layer]
D --> E[Blockchain Service]
D --> F[API Service]
E --> G[Smart Contracts]
F --> H[Backend API]
I[IPFS/Arweave] <--> E
J[AI Services] <--> F
subgraph "Frontend"
A
B
C
end
subgraph "Business Logic"
D
E
F
end
subgraph "Data Layer"
G
H
I
J
end
The application follows a layered architecture with clear separation of concerns:
The presentation layer is built with React and follows atomic design principles:
graph TD
A[Pages] --> B[Templates]
B --> C[Organisms]
C --> D[Molecules]
D --> E[Atoms]
- Atoms: Smallest UI components (buttons, inputs, icons)
- Molecules: Simple UI components formed by atoms (search form, menu items)
- Organisms: Complex UI components (headers, story cards)
- Templates: Page layouts without specific content
- Pages: Complete views with specific content
The business logic is encapsulated in services that handle:
- Authentication and authorization
- Web3 interactions (wallet connections, contract calls)
- Content management (creation, retrieval, updates)
- Notifications and messaging
The data layer consists of:
- Blockchain: Smart contracts for ownership and transactions
- Decentralized Storage: IPFS/Arweave for content storage
- API Services: Backend services for additional functionality
- Local Storage: For caching and offline capabilities
- Framework: React 18 with TypeScript
- State Management: Redux Toolkit with RTK Query
- Styling: Tailwind CSS with custom theming
- Component Library: Custom component system
- Routing: React Router v6
- Form Handling: Formik with Yup validation
- Wallet Connection: ethers.js v6
- Contract Interaction: ethers.js + custom hooks
- Storage: IPFS via web3.storage
- Identity: ENS integration
- Build Tool: Vite
- Package Manager: npm
- Linting: ESLint with custom ruleset
- Formatting: Prettier
- Testing: Jest, React Testing Library, Cypress
- CI/CD: GitHub Actions
- HTTP Client: Axios with interceptors
- Authentication: JWT + Wallet signatures
- Caching: RTK Query with localStorage persistence
ctory/
├── public/ # Static files
├── src/
│ ├── api/ # API client and endpoints
│ ├── assets/ # Static assets
│ ├── components/ # Reusable UI components
│ │ ├── atoms/ # Basic UI elements
│ │ ├── molecules/ # Composite components
│ │ ├── organisms/ # Complex components
│ │ └── templates/ # Page templates
│ ├── constants/ # Application constants
│ ├── contexts/ # React contexts
│ ├── core/ # Core configuration
│ ├── features/ # Feature-based modules
│ │ ├── auth/ # Authentication feature
│ │ ├── stories/ # Stories feature
│ │ └── profile/ # User profile feature
│ ├── hooks/ # Custom React hooks
│ ├── layouts/ # Layout components
│ ├── pages/ # Page components
│ ├── routes/ # Routing configuration
│ ├── services/ # Business logic services
│ │ ├── auth/ # Authentication service
│ │ ├── blockchain/ # Blockchain service
│ │ └── ipfs/ # IPFS service
│ ├── store/ # Redux store and slices
│ ├── styles/ # Global styles
│ ├── types/ # TypeScript types
│ ├── utils/ # Utility functions
│ ├── App.tsx # Main App component
│ └── index.tsx # Entry point
├── .env.example # Example environment variables
├── .eslintrc.js # ESLint configuration
├── .gitignore # Git ignore rules
├── .nvmrc # Node version
├── LICENSE # MIT License
├── package.json # Package configuration
├── postcss.config.js # PostCSS configuration
├── README.md # Project documentation
├── tailwind.config.js # Tailwind CSS configuration
├── tsconfig.json # TypeScript configuration
└── vite.config.ts # Vite configuration
The authentication flow combines traditional Web2 authentication with Web3 wallet-based authentication:
sequenceDiagram
participant User
participant App
participant Wallet
participant API
participant Contract
User->>App: Click "Connect Wallet"
App->>Wallet: Request Connection
Wallet-->>User: Approve Connection?
User-->>Wallet: Approve
Wallet-->>App: Return Address
App->>API: Generate Challenge
API-->>App: Challenge String
App->>Wallet: Request Signature
Wallet-->>User: Sign Message?
User-->>Wallet: Approve
Wallet-->>App: Return Signature
App->>API: Verify Signature
API->>Contract: Verify User Exists
API-->>App: JWT Token + User Data
App->>User: Show Dashboard
The content creation and publishing flow:
sequenceDiagram
participant Author
participant App
participant IPFS
participant Contract
Author->>App: Create Content
App->>Author: Draft Saved Locally
Author->>App: Publish Content
App->>IPFS: Store Content
IPFS-->>App: Content CID
App->>Contract: Mint Story NFT
Contract-->>App: Transaction Receipt
App-->>Author: Publication Confirmed
The platform uses a hybrid recommendation system combining on-chain data with traditional recommendation algorithms:
graph TD
A[User Activity] --> B[On-chain Analysis]
A --> C[Content Metadata]
D[Reading History] --> E[ML Model]
B --> E
C --> E
E --> F[Personalized Feed]
G[Community Trends] --> F
H[Curator Signals] --> F
Multiple monetization strategies are supported:
- Direct Sales: One-time purchases of NFT content
- Subscriptions: Recurring access passes
- Tipping: Reader-to-author direct payments
- Patronage: Long-term support commitments
- Revenue Sharing: Collaborative creation rewards
The application uses unidirectional data flow through Redux with side effects managed by middleware:
graph LR
A[User Action] --> B[Action Creator]
B --> C[Middleware]
C --> D[Reducer]
D --> E[Store Update]
E --> F[Component Re-render]
C --> G[Side Effect]
G --> H[API Call]
G --> I[Blockchain Tx]
H --> B
I --> B
graph TD
A[Component] --> B[Hooks]
B --> C[Selectors]
C --> D[Store]
B --> E[Actions]
E --> F[Thunks/Middleware]
F --> G[API Services]
F --> H[Blockchain Services]
G --> I[External APIs]
H --> J[Smart Contracts]
F --> K[Reducers]
K --> D
- Node.js v18.19.0 or higher
- npm v9 or higher
- MetaMask or other Web3 wallet
- Modern web browser (Chrome, Firefox, Brave, Edge)
- Clone the repository
git clone https://github.com/CtoryAI/Ctory.git
cd Ctory
- Install dependencies
npm install
- Create environment file
cp .env.example .env
- Configure environment variables
VITE_API_URL=http://localhost:3000/api
VITE_CONTRACT_ADDRESS=0x...
VITE_CONTRACT_ABI=[]
VITE_IPFS_GATEWAY=https://ipfs.io
VITE_IPFS_PROJECT_ID=your_ipfs_project_id
VITE_IPFS_PROJECT_SECRET=your_ipfs_project_secret
VITE_AI_API_KEY=your_ai_api_key
VITE_AI_API_URL=https://api.example.com
- Start development server
npm run dev
// Example of using the wallet hook
const { connect, disconnect, address, isConnected } = useWallet();
// In your component
return (
<Button
onClick={isConnected ? disconnect : connect}
>
{isConnected ? `Disconnect (${shortenAddress(address)})` : 'Connect Wallet'}
</Button>
);
// Example of interacting with a smart contract
const { contract } = useContract(CONTRACT_ADDRESS, CONTRACT_ABI);
const publishStory = async (metadataURI: string) => {
if (!contract) return;
try {
const tx = await contract.mintStory(metadataURI);
await tx.wait();
// Handle success
} catch (error) {
// Handle error
}
};
// Example of storing content on IPFS
const { storeContent, isLoading } = useIPFS();
const saveStory = async (content: StoryContent) => {
try {
const cid = await storeContent(content);
return `ipfs://${cid}`;
} catch (error) {
// Handle error
return null;
}
};
// Example of protected route component usage
const AppRoutes = () => (
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/dashboard" element={
<ProtectedRoute>
<DashboardPage />
</ProtectedRoute>
} />
<Route path="/create" element={
<ProtectedRoute>
<CreateStoryPage />
</ProtectedRoute>
} />
</Routes>
);
- All contracts audited by independent security firms
- Formal verification for critical functions
- Extensive test coverage with property-based testing
- Time-locked admin functions with multi-sig requirements
- Input validation on both client and server side
- Content Security Policy implementation
- Regular dependency audits
- XSS and CSRF protection
- Hardware wallet support
- Transaction simulation before submission
- Gas estimation and optimization
- Clear transaction information display
The application uses dynamic imports to split code by routes:
const HomePage = lazy(() => import('./pages/HomePage'));
const DashboardPage = lazy(() => import('./pages/DashboardPage'));
- Images are optimized at build time
- SVG optimization with SVGO
- Critical CSS inlining
- Font subsetting and optimization
- Selective state persistence
- Normalized Redux store structure
- Memoized selectors with Reselect
- Optimistic UI updates
// Example of a reducer test
describe('authReducer', () => {
it('should handle user login', () => {
const initialState = { user: null, isAuthenticated: false };
const action = setUser({ id: '1', address: '0x...' });
const newState = authReducer(initialState, action);
expect(newState.user).toEqual({ id: '1', address: '0x...' });
expect(newState.isAuthenticated).toBe(true);
});
});
Integration tests focus on interaction between components and services:
// Example integration test
test('user can connect wallet and login', async () => {
renderWithProviders(<LoginPage />);
await userEvent.click(screen.getByText('Connect Wallet'));
// Mock wallet connection response
expect(await screen.findByText('Dashboard')).toBeInTheDocument();
expect(mockNavigate).toHaveBeenCalledWith('/dashboard');
});
E2E tests use Cypress to simulate user journeys:
// Example Cypress test
describe('Story Creation', () => {
beforeEach(() => {
cy.login();
cy.visit('/create');
});
it('allows creating and publishing a story', () => {
cy.get('input[name="title"]').type('My Test Story');
cy.get('textarea[name="content"]').type('This is the content of my test story.');
cy.get('button[type="submit"]').click();
cy.url().should('include', '/story/');
cy.contains('My Test Story').should('be.visible');
});
});
npm run build
This creates optimized production files in the dist
directory.
GitHub Actions workflows:
name: CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- run: npm ci
- run: npm run lint
- run: npm test
- run: npm run build
- Decentralized: IPFS + ENS
- Traditional: AWS, Vercel, Netlify
- Hybrid: Centralized API + Decentralized Frontend
- Initial platform launch
- Basic Web3 integration
- Story creation and publishing
- Enhanced user profiles
- Social features (comments, likes)
- Mobile responsive design
- Multi-chain support
- Improved recommendation system
- Advanced monetization options
- Governance system
- Creator communities
- Enhanced analytics
- Mobile applications
- AI-assisted creation tools
- Cross-platform integration
Q: What is Ctory?
A: Ctory is a decentralized platform that empowers storytellers to create, share, and monetize their content with full ownership and control through blockchain technology.
Q: Why blockchain?
A: Blockchain provides verifiable ownership, transparent monetization, censorship resistance, and direct creator-audience relationships without intermediaries.
Q: Which blockchains are supported?
A: Initially Ethereum and Polygon, with plans to support additional EVM-compatible chains.
Q: How is content stored?
A: Content is stored on IPFS with metadata and ownership recorded on-chain.
Q: Is a wallet required?
A: A Web3 wallet is required for publishing and monetization features, but reading content can be done without one.
We welcome contributions from the community! Here's how to get started:
- Fork the repository
- Create your feature branch:
git checkout -b feature/amazing-feature
- Commit your changes:
git commit -m 'Add amazing feature'
- Push to the branch:
git push origin feature/amazing-feature
- Open a Pull Request
- Follow the established code style
- Write tests for new features
- Update documentation
- Make atomic commits with clear messages
This project is licensed under the MIT License - see the LICENSE file for details.
- Website: www.ctory.xyz
- Twitter: @Ctory_
- GitHub: CtoryAI/Ctory