Skip to main content

Integration Guide

Complete guide for integrating EvoBin decentralized storage into your applications.

Overview​

EvoBin provides multiple integration points for developers:

  1. Web Interface - Users interact directly with EvoBin
  2. Extension API - Connect user wallets and identities
  3. Dash Platform SDK - Direct blockchain interaction
  4. 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​

<!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​

Next Steps​

  1. Test thoroughly on testnet before mainnet deployment
  2. Monitor performance and adjust chunk sizes as needed
  3. Implement backup strategies for critical data
  4. Stay updated with EvoBin API changes
  5. 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.