Integration Guide
Complete guide for integrating EvoBin decentralized storage into your applications.
Overview​
EvoBin provides multiple integration points for developers:
- Web Interface - Users interact directly with EvoBin
- Extension API - Connect user wallets and identities
- Dash Platform SDK - Direct blockchain interaction
- REST API (coming soon) - Server-side integration
Integration Architecture​
graph TB
subgraph "Your Application"
A[Frontend] --> B[EvoBin Integration Layer]
C[Backend] --> B
end
subgraph "EvoBin Platform"
B --> D[Dash Platform Extension]
B --> E[Dash Platform SDK]
D --> F[User Wallet/Identity]
E --> G[Dash Platform Network]
end
G --> H[Data Contracts]
G --> I[Documents]
G --> J[Identities]
Integration Methods​
Method 1: Direct Web Integration (Recommended)​
<!DOCTYPE html>
<html>
<head>
<title>Your App with EvoBin</title>
<script>
// Check for Dash Platform Extension
async function initializeEvoBin() {
if (!window.dashPlatformExtension) {
// Guide user to install extension
showInstallPrompt()
return
}
try {
// Connect to extension
const ex = window.dashPlatformExtension
const { currentIdentity, identities } = await ex.signer.connect()
// SDK is now injected
const sdk = window.dashPlatformSDK
// Ready to use EvoBin
console.log('Connected as:', currentIdentity)
return { ex, sdk, currentIdentity }
} catch (error) {
console.error('Failed to connect:', error)
throw error
}
}
function showInstallPrompt() {
// Show user-friendly installation instructions
const modal = document.createElement('div')
modal.innerHTML = `
<div style="position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.5); display:flex; align-items:center; justify-content:center; z-index:1000;">
<div style="background:white; padding:2rem; border-radius:8px; max-width:500px;">
<h2>Dash Platform Extension Required</h2>
<p>To use EvoBin storage, please install the Dash Platform Extension.</p>
<a href="https://chromewebstore.google.com/detail/dash-platform-extension/odmphbcnlldggfhcpdjgnlhbehoicdnf"
target="_blank"
style="display:inline-block; background:#8b5cf6; color:white; padding:0.5rem 1rem; border-radius:4px; text-decoration:none;">
Install Extension
</a>
<button onclick="this.parentElement.parentElement.remove()"
style="margin-left:1rem; padding:0.5rem 1rem; border:1px solid #ccc; border-radius:4px;">
Later
</button>
</div>
</div>
`
document.body.appendChild(modal)
}
</script>
</head>
<body>
<!-- Your app content -->
<button onclick="initializeEvoBin()">Connect EvoBin</button>
</body>
</html>
Method 2: NPM Package Integration​
# Install EvoBin SDK (when available)
npm install @evobin/sdk
import { EvoBinClient } from '@evobin/sdk'
class YourApplication {
private evoBin: EvoBinClient
constructor() {
this.evoBin = new EvoBinClient({
network: 'testnet', // or 'mainnet'
autoConnect: true
})
}
async initialize() {
try {
// Connect to user's wallet
await this.evoBin.connect()
// Check if user has storage contract
const hasStorage = await this.evoBin.hasStorageContract()
if (!hasStorage) {
// Create storage contract for user
await this.evoBin.createStorageContract()
}
return this.evoBin
} catch (error) {
console.error('EvoBin initialization failed:', error)
throw error
}
}
async uploadFile(file: File): Promise<string> {
// Encrypt and upload file
const result = await this.evoBin.uploadFile(file, {
encryption: 'AES-256-GCM',
chunkSize: 1024 * 1024, // 1MB chunks
redundancy: 3 // Store 3 copies
})
return result.fileId
}
async shareFile(fileId: string, options: ShareOptions): Promise<string> {
// Create share link
const shareLink = await this.evoBin.createShareLink(fileId, {
permission: options.permission || 'view',
expiresAt: options.expiresAt,
password: options.password,
maxDownloads: options.maxDownloads
})
return shareLink
}
}
interface ShareOptions {
permission?: 'view' | 'download' | 'edit'
expiresAt?: Date
password?: string
maxDownloads?: number
}
Method 3: React/Next.js Integration​
components/EvoBinProvider.tsx
'use client'
import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react'
interface EvoBinContextType {
isConnected: boolean
identity: string | null
connect: () => Promise<void>
disconnect: () => Promise<void>
uploadFile: (file: File) => Promise<string>
getFile: (fileId: string) => Promise<File>
shareFile: (fileId: string, options: ShareOptions) => Promise<string>
}
const EvoBinContext = createContext<EvoBinContextType | null>(null)
interface EvoBinProviderProps {
children: ReactNode
}
export function EvoBinProvider({ children }: EvoBinProviderProps) {
const [isConnected, setIsConnected] = useState(false)
const [identity, setIdentity] = useState<string | null>(null)
const [sdk, setSdk] = useState<any>(null)
useEffect(() => {
// Check for existing connection
const checkConnection = async () => {
if (typeof window === 'undefined') return
const connectionData = localStorage.getItem('dash_connection')
if (connectionData) {
try {
const { identity } = JSON.parse(connectionData)
setIdentity(identity)
setIsConnected(true)
// SDK should be injected by extension
if (window.dashPlatformSDK) {
setSdk(window.dashPlatformSDK)
}
} catch (error) {
console.error('Failed to restore connection:', error)
}
}
}
checkConnection()
}, [])
const connect = async () => {
if (typeof window === 'undefined') {
throw new Error('EvoBin only works in browser environments')
}
if (!window.dashPlatformExtension?.signer) {
throw new Error('Dash Platform Extension not found. Please install it.')
}
try {
const { currentIdentity } = await window.dashPlatformExtension.signer.connect()
// Store connection
localStorage.setItem('dash_connection', JSON.stringify({
identity: currentIdentity,
connected: true,
timestamp: Date.now()
}))
setIdentity(currentIdentity)
setIsConnected(true)
setSdk(window.dashPlatformSDK)
} catch (error) {
console.error('Connection failed:', error)
throw error
}
}
const disconnect = async () => {
localStorage.removeItem('dash_connection')
setIdentity(null)
setIsConnected(false)
setSdk(null)
// Call extension disconnect if available
if (window.dashPlatformExtension?.disconnectAccount) {
await window.dashPlatformExtension.disconnectAccount()
}
}
const uploadFile = async (file: File): Promise<string> => {
if (!isConnected || !sdk) {
throw new Error('Not connected to EvoBin')
}
// Implement file upload logic
// This is a simplified example
const formData = new FormData()
formData.append('file', file)
// In reality, you would:
// 1. Encrypt file client-side
// 2. Create data contract document
// 3. Store encrypted chunks
// 4. Return file ID
const mockFileId =`file_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
return mockFileId
}
const getFile = async (fileId: string): Promise<File> => {
if (!isConnected || !sdk) {
throw new Error('Not connected to EvoBin')
}
// Implement file retrieval logic
// This would:
// 1. Fetch encrypted chunks
// 2. Decrypt client-side
// 3. Reconstruct file
throw new Error('File retrieval not implemented in this example')
}
const shareFile = async (fileId: string, options: ShareOptions): Promise<string> => {
if (!isConnected || !sdk) {
throw new Error('Not connected to EvoBin')
}
// Create share document in blockchain
// Return share URL
const shareUrl =`https://evobin.xyz/share/${fileId}?token=${Math.random().toString(36).substr(2)}`
return shareUrl
}
return (
<EvoBinContext.Provider value={{
isConnected,
identity,
connect,
disconnect,
uploadFile,
getFile,
shareFile
}}>
{children}
</EvoBinContext.Provider>
)
}
export function useEvoBin() {
const context = useContext(EvoBinContext)
if (!context) {
throw new Error('useEvoBin must be used within EvoBinProvider')
}
return context
}
components/FileUpload.tsx
'use client'
import { useState } from 'react'
import { useEvoBin } from './EvoBinProvider'
export function FileUpload() {
const { isConnected, uploadFile } = useEvoBin()
const [uploading, setUploading] = useState(false)
const [progress, setProgress] = useState(0)
const [fileId, setFileId] = useState<string | null>(null)
const handleFileSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0]
if (!file) return
if (!isConnected) {
alert('Please connect to EvoBin first')
return
}
setUploading(true)
setProgress(0)
try {
// Simulate progress (in real app, chunk uploads)
const interval = setInterval(() => {
setProgress(prev => {
if (prev >= 90) {
clearInterval(interval)
return 90
}
return prev + 10
})
}, 100)
const id = await uploadFile(file)
clearInterval(interval)
setProgress(100)
setFileId(id)
setTimeout(() => {
setUploading(false)
setProgress(0)
}, 1000)
} catch (error) {
console.error('Upload failed:', error)
alert('Upload failed: ' + error.message)
setUploading(false)
setProgress(0)
}
}
return (
<div className="file-upload">
<input
type="file"
onChange={handleFileSelect}
disabled={uploading || !isConnected}
/>
{uploading && (
<div className="progress-bar">
<div
className="progress-fill"
style={{ width:`${progress}%` }}
/>
</div>
)}
{fileId && (
<div className="success-message">
File uploaded! ID: {fileId}
</div>
)}
</div>
)
}
Authentication Flow​
OAuth-style Integration​
class EvoBinAuth {
private redirectUri: string
private clientId: string
constructor(options: { redirectUri: string; clientId: string }) {
this.redirectUri = options.redirectUri
this.clientId = options.clientId
}
async authenticate(): Promise<AuthResponse> {
// Check if extension is available
if (!window.dashPlatformExtension) {
return {
success: false,
error: 'extension_not_installed',
message: 'Dash Platform Extension not found'
}
}
try {
const ex = window.dashPlatformExtension
// Request connection
const { currentIdentity, identities } = await ex.signer.connect()
// Generate session token
const sessionToken = this.generateSessionToken(currentIdentity)
// Store session
localStorage.setItem('evobin_session', JSON.stringify({
identity: currentIdentity,
sessionToken,
expiresAt: Date.now() + (24 * 60 * 60 * 1000) // 24 hours
}))
return {
success: true,
identity: currentIdentity,
sessionToken,
identities
}
} catch (error) {
return {
success: false,
error: 'authentication_failed',
message: error.message
}
}
}
async getSession(): Promise<Session | null> {
const sessionData = localStorage.getItem('evobin_session')
if (!sessionData) return null
const session: Session = JSON.parse(sessionData)
// Check if session expired
if (session.expiresAt < Date.now()) {
localStorage.removeItem('evobin_session')
return null
}
// Validate with extension
try {
const ex = window.dashPlatformExtension
const { currentIdentity } = await ex.signer.connect()
if (currentIdentity !== session.identity) {
localStorage.removeItem('evobin_session')
return null
}
return session
} catch (error) {
localStorage.removeItem('evobin_session')
return null
}
}
private generateSessionToken(identity: string): string {
// In production, this would be a JWT or similar
return btoa(JSON.stringify({
identity,
timestamp: Date.now(),
nonce: Math.random().toString(36).substr(2, 9)
}))
}
}
interface AuthResponse {
success: boolean
identity?: string
sessionToken?: string
identities?: string[]
error?: string
message?: string
}
interface Session {
identity: string
sessionToken: string
expiresAt: number
}
File Operations​
Upload with Progress​
interface UploadOptions {
encryption?: 'AES-256-GCM' | 'ChaCha20-Poly1305'
chunkSize?: number
redundancy?: number
onProgress?: (progress: number) => void
metadata?: Record<string, any>
}
class FileUploader {
async uploadFile(file: File, options: UploadOptions = {}): Promise<UploadResult> {
const {
encryption = 'AES-256-GCM',
chunkSize = 1024 * 1024, // 1MB
redundancy = 3,
onProgress,
metadata = {}
} = options
// Generate encryption key
const encryptionKey = await this.generateEncryptionKey()
// Split file into chunks
const chunks = await this.splitFile(file, chunkSize)
const totalChunks = chunks.length
// Encrypt and upload each chunk
const uploadedChunks: string[] = []
for (let i = 0; i < chunks.length; i++) {
const chunk = chunks[i]
// Encrypt chunk
const encryptedChunk = await this.encryptChunk(chunk, encryptionKey, encryption)
// Create document for chunk
const chunkDocument = {
chunkIndex: i,
totalChunks,
fileHash: await this.calculateFileHash(file),
encryptedData: encryptedChunk,
encryptionAlgorithm: encryption,
redundancy,
metadata
}
// Upload to blockchain
const chunkId = await this.uploadToBlockchain(chunkDocument)
uploadedChunks.push(chunkId)
// Update progress
const progress = Math.round(((i + 1) / totalChunks) * 100)
onProgress?.(progress)
}
// Create file manifest
const manifest = {
fileName: file.name,
fileSize: file.size,
fileType: file.type,
chunks: uploadedChunks,
encryptionKey: await this.encryptKeyForStorage(encryptionKey),
createdAt: Date.now(),
metadata
}
// Upload manifest
const manifestId = await this.uploadToBlockchain(manifest)
return {
fileId: manifestId,
chunks: uploadedChunks,
encryption,
size: file.size,
uploadedAt: Date.now()
}
}
private async splitFile(file: File, chunkSize: number): Promise<ArrayBuffer[]> {
const chunks: ArrayBuffer[] = []
const fileSize = file.size
let offset = 0
while (offset < fileSize) {
const chunk = file.slice(offset, offset + chunkSize)
const arrayBuffer = await chunk.arrayBuffer()
chunks.push(arrayBuffer)
offset += chunkSize
}
return chunks
}
private async generateEncryptionKey(): Promise<CryptoKey> {
return await crypto.subtle.generateKey(
{
name: 'AES-GCM',
length: 256
},
true,
['encrypt', 'decrypt']
)
}
private async encryptChunk(ArrayBuffer,
key: CryptoKey,
algorithm: string
): Promise<ArrayBuffer> {
const iv = crypto.getRandomValues(new Uint8Array(12))
return await crypto.subtle.encrypt(
{
name: algorithm,
iv: iv
},
key,
data
)
}
private async calculateFileHash(file: File): Promise<string> {
const buffer = await file.arrayBuffer()
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer)
return Array.from(new Uint8Array(hashBuffer))
.map(b => b.toString(16).padStart(2, '0'))
.join('')
}
private async encryptKeyForStorage(key: CryptoKey): Promise<string> {
// In production, this would use a user-specific key encryption key
const exported = await crypto.subtle.exportKey('raw', key)
return btoa(String.fromCharCode(...new Uint8Array(exported)))
}
private async uploadToBlockchain(data: any): Promise<string> {
// This is where you'd integrate with Dash Platform SDK
// For now, return a mock ID
return`doc_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
}
}
interface UploadResult {
fileId: string
chunks: string[]
encryption: string
size: number
uploadedAt: number
}
Download & Decryption​
class FileDownloader {
async downloadFile(fileId: string, onProgress?: (progress: number) => void): Promise<File> {
// Fetch file manifest
const manifest = await this.fetchManifest(fileId)
// Decrypt encryption key
const encryptionKey = await this.decryptEncryptionKey(manifest.encryptionKey)
// Download and decrypt chunks
const chunkBuffers: ArrayBuffer[] = []
for (let i = 0; i < manifest.chunks.length; i++) {
const chunkId = manifest.chunks[i]
const encryptedChunk = await this.fetchChunk(chunkId)
const decryptedChunk = await this.decryptChunk(
encryptedChunk,
encryptionKey,
manifest.encryptionAlgorithm
)
chunkBuffers.push(decryptedChunk)
// Update progress
const progress = Math.round(((i + 1) / manifest.chunks.length) * 100)
onProgress?.(progress)
}
// Combine chunks
const totalSize = chunkBuffers.reduce((sum, buffer) => sum + buffer.byteLength, 0)
const combinedBuffer = new Uint8Array(totalSize)
let offset = 0
for (const buffer of chunkBuffers) {
combinedBuffer.set(new Uint8Array(buffer), offset)
offset += buffer.byteLength
}
// Create file object
return new File([combinedBuffer], manifest.fileName, {
type: manifest.fileType,
lastModified: manifest.createdAt
})
}
private async fetchManifest(fileId: string): Promise<any> {
// Fetch from blockchain
// This is a mock implementation
return {
fileName: 'example.txt',
fileType: 'text/plain',
chunks: ['chunk1', 'chunk2', 'chunk3'],
encryptionKey: 'encrypted-key-here',
encryptionAlgorithm: 'AES-256-GCM',
createdAt: Date.now()
}
}
private async decryptEncryptionKey(encryptedKey: string): Promise<CryptoKey> {
// Decrypt using user's master key
const keyData = atob(encryptedKey)
const keyBuffer = new Uint8Array(keyData.length)
for (let i = 0; i < keyData.length; i++) {
keyBuffer[i] = keyData.charCodeAt(i)
}
return await crypto.subtle.importKey(
'raw',
keyBuffer,
{ name: 'AES-GCM', length: 256 },
true,
['encrypt', 'decrypt']
)
}
private async fetchChunk(chunkId: string): Promise<ArrayBuffer> {
// Fetch from blockchain
// Mock implementation
return new ArrayBuffer(1024)
}
private async decryptChunk(
encryptedData: ArrayBuffer,
key: CryptoKey,
algorithm: string
): Promise<ArrayBuffer> {
// Assuming IV is prepended to data
const iv = encryptedData.slice(0, 12)
const ciphertext = encryptedData.slice(12)
return await crypto.subtle.decrypt(
{
name: algorithm,
iv: iv
},
key,
ciphertext
)
}
}
Error Handling & Recovery​
class EvoBinError extends Error {
constructor(
message: string,
public code: string,
public details?: any
) {
super(message)
this.name = 'EvoBinError'
}
}
class EvoBinClientWithRetry {
private maxRetries = 3
private retryDelay = 1000 // 1 second
async withRetry<T>(
operation: () => Promise<T>,
operationName: string
): Promise<T> {
let lastError: Error
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
try {
return await operation()
} catch (error) {
lastError = error
// Don't retry certain errors
if (this.isNonRetryableError(error)) {
throw this.wrapError(error, operationName)
}
// Exponential backoff
const delay = this.retryDelay * Math.pow(2, attempt - 1)
console.warn(
`Attempt ${attempt} failed for ${operationName}, ` +
`retrying in ${delay}ms:`,
error.message
)
if (attempt < this.maxRetries) {
await this.sleep(delay)
}
}
}
throw this.wrapError(lastError, operationName)
}
private isNonRetryableError(error: Error): boolean {
const nonRetryableCodes = [
'INSUFFICIENT_BALANCE',
'INVALID_SIGNATURE',
'PERMISSION_DENIED',
'VALIDATION_ERROR'
]
return nonRetryableCodes.some(code =>
error.message.includes(code) ||
(error as any).code === code
)
}
private wrapError(error: Error, operationName: string): EvoBinError {
let code = 'UNKNOWN_ERROR'
let message = error.message
if (error.message.includes('insufficient balance')) {
code = 'INSUFFICIENT_BALANCE'
message = 'Insufficient credits to perform this operation'
} else if (error.message.includes('signature')) {
code = 'INVALID_SIGNATURE'
message = 'Transaction signature is invalid'
} else if (error.message.includes('permission denied')) {
code = 'PERMISSION_DENIED'
message = 'You do not have permission to perform this action'
} else if (error.message.includes('validation')) {
code = 'VALIDATION_ERROR'
message = 'Data validation failed'
} else if (error.message.includes('network')) {
code = 'NETWORK_ERROR'
message = 'Network connection issue, please try again'
}
return new EvoBinError(
`${operationName} failed: ${message}`,
code,
{ originalError: error }
)
}
private sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms))
}
// Example usage
async uploadFileWithRetry(file: File): Promise<string> {
return this.withRetry(
async () => {
// Your upload logic here
return 'file-id'
},
'uploadFile'
)
}
}
Testing Your Integration​
import { EvoBinClient } from '@evobin/sdk'
describe('EvoBin Integration Tests', () => {
let client: EvoBinClient
beforeAll(async () => {
// Use testnet for testing
client = new EvoBinClient({
network: 'testnet',
autoConnect: false
})
})
test('should connect to extension', async () => {
const result = await client.connect()
expect(result).toHaveProperty('identity')
expect(result).toHaveProperty('identities')
expect(result.identities).toContain(result.identity)
})
test('should upload and download file', async () => {
// Create test file
const testContent = 'Hello, EvoBin!'
const testFile = new File([testContent], 'test.txt', {
type: 'text/plain'
})
// Upload
const fileId = await client.uploadFile(testFile, {
encryption: 'AES-256-GCM',
onProgress: (progress) => {
console.log(`Upload progress: ${progress}%`)
}
})
expect(fileId).toBeDefined()
expect(typeof fileId).toBe('string')
// Download
const downloadedFile = await client.downloadFile(fileId, {
onProgress: (progress) => {
console.log(`Download progress: ${progress}%`)
}
})
expect(downloadedFile).toBeInstanceOf(File)
expect(downloadedFile.name).toBe('test.txt')
expect(downloadedFile.type).toBe('text/plain')
// Verify content
const content = await downloadedFile.text()
expect(content).toBe(testContent)
})
test('should handle upload errors gracefully', async () => {
// Try to upload empty file
const emptyFile = new File([], 'empty.txt')
await expect(client.uploadFile(emptyFile)).rejects.toThrow(
/File is empty|Invalid file size/
)
})
test('should share and verify permissions', async () => {
const testFile = new File(['Test content'], 'share-test.txt')
const fileId = await client.uploadFile(testFile)
// Create share link with restrictions
const shareLink = await client.shareFile(fileId, {
permission: 'view',
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours
maxDownloads: 5
})
expect(shareLink).toMatch(/^https?:\/\//)
// Verify share link properties
const shareInfo = await client.getShareInfo(shareLink)
expect(shareInfo.permission).toBe('view')
expect(shareInfo.maxDownloads).toBe(5)
expect(shareInfo.expiresAt).toBeInstanceOf(Date)
})
})
Production Checklist​
Before deploying your integration to production:
✅ Security​
- Use HTTPS in production
- Implement proper error handling
- Validate all user inputs
- Use secure random number generation
- Encrypt sensitive data client-side
- Implement rate limiting
✅ Performance​
- Implement chunked uploads for large files
- Use compression where appropriate
- Cache frequently accessed data
- Implement connection pooling
- Monitor and optimize gas usage
✅ User Experience​
- Provide clear error messages
- Implement loading states
- Add progress indicators
- Support offline functionality
- Handle network interruptions gracefully
✅ Monitoring​
- Set up error tracking (Sentry, LogRocket)
- Monitor performance metrics
- Track user analytics (privacy-focused)
- Set up alerts for critical failures
- Log important operations
Support & Resources​
- Documentation: docs.evobin.xyz
- GitHub: github.com/evobin
- Discord: Join our community
- Email: support@evobin.xyz
- Issue Tracker: GitHub Issues
Next Steps​
- Test thoroughly on testnet before mainnet deployment
- Monitor performance and adjust chunk sizes as needed
- Implement backup strategies for critical data
- Stay updated with EvoBin API changes
- Join our community for support and updates Remember to always test with small amounts first and gradually increase as you become more confident with the integration.