Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions backend/.env.exemple
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Admin Wallet Configuration
# This wallet owns the Cap object and can add users to the whitelist
ADMIN_PRIVATE_KEY=your_private_key_here
ADMIN_ADDRESS=0x904f64f755764162a228a7da49b1288160597165ec60ebbf5fb9a94957db76c3

# Whitelist Contract IDs
PACKAGE_ID=0x0fe074f026b27ea8617d326dc732b635a762bb64e23b943bafc7ac49f8e9eb52
WHITELIST_ID=0x7d4fdefe79f2b7332672e2289b331dcc445a47d2379a39bed95adbe91e5fcc7d
CAP_ID=0xcc9cb3bedca6f9e34f69420832b18cc24887dea30fe6616feec08113769051c5

# Sui Network
SUI_NETWORK=testnet
2 changes: 1 addition & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ COPY . .
RUN pnpm build

# Expose the port
EXPOSE 3000
EXPOSE 8000

# Start the application
CMD ["pnpm", "start"]
4 changes: 4 additions & 0 deletions backend/docker-compose.dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
services:
backend:
ports:
- "8000:8000"
10 changes: 10 additions & 0 deletions backend/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
services:
backend:
build:
context: .
dockerfile: Dockerfile
env_file:
- .env
environment:
- PORT=8000
restart: unless-stopped
6 changes: 3 additions & 3 deletions backend/move/Move.lock
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ dependencies = [
]

[move.toolchain-version]
compiler-version = "1.58.1"
compiler-version = "1.61.1"
edition = "2024.beta"
flavor = "sui"

[env]

[env.testnet]
chain-id = "4c78adac"
original-published-id = "0xc0ce2d9449d970ad1a5e85f6c0d5e214be416ba62a3fb8965a5a2f3259df73c6"
latest-published-id = "0xc0ce2d9449d970ad1a5e85f6c0d5e214be416ba62a3fb8965a5a2f3259df73c6"
original-published-id = "0xb30915eb3f8b706ca0d46981b5e23e74003f65bb521b641c3f53fe1a652062b7"
latest-published-id = "0xb30915eb3f8b706ca0d46981b5e23e74003f65bb521b641c3f53fe1a652062b7"
published-version = "1"
237 changes: 237 additions & 0 deletions backend/move/sources/user_data.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
// Copyright (c), Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

/// Module to store user data (strategies, execution history) on-chain
/// Replaces localStorage usage for decentralized data storage
module startHack::user_data {
use sui::table::{Self, Table};
use std::string::String;

// Error codes
const ENotOwner: u64 = 1;
const EStrategyNotFound: u64 = 2;
const EHistoryNotFound: u64 = 3;

// Events
public struct StrategySaved has copy, drop {
user: address,
strategy_id: u64,
timestamp: u64,
}

public struct StrategyDeleted has copy, drop {
user: address,
strategy_id: u64,
}

public struct ExecutionRecorded has copy, drop {
user: address,
execution_id: u64,
timestamp: u64,
}

public struct ExecutionHistoryCleared has copy, drop {
user: address,
}

// Saved strategy data structure
public struct SavedStrategy has store, drop, copy {
id: u64,
name: String,
description: String,
strategy_json: String, // JSON stringified strategy
created_at: u64,
updated_at: u64,
}

// Execution history entry
public struct ExecutionEntry has store, drop, copy {
id: u64,
workflow_name: String,
workflow_id: String,
status: String, // "success", "failed", "pending"
tx_digest: String,
timestamp: u64,
gas_used: u64,
result_data: String, // JSON stringified result
}

// User data storage object
public struct UserDataStorage has key {
id: UID,
owner: address,
saved_strategies: Table<u64, SavedStrategy>,
strategy_counter: u64,
execution_history: Table<u64, ExecutionEntry>,
execution_counter: u64,
}

// Create a new user data storage
public entry fun create_storage(ctx: &mut TxContext) {
let storage = UserDataStorage {
id: object::new(ctx),
owner: ctx.sender(),
saved_strategies: table::new(ctx),
strategy_counter: 0,
execution_history: table::new(ctx),
execution_counter: 0,
};
transfer::transfer(storage, ctx.sender());
}

// Save a new strategy
public entry fun save_strategy(
storage: &mut UserDataStorage,
name: String,
description: String,
strategy_json: String,
ctx: &mut TxContext
) {
assert!(storage.owner == ctx.sender(), ENotOwner);

let timestamp = ctx.epoch_timestamp_ms();
let strategy_id = storage.strategy_counter;

let strategy = SavedStrategy {
id: strategy_id,
name,
description,
strategy_json,
created_at: timestamp,
updated_at: timestamp,
};

storage.saved_strategies.add(strategy_id, strategy);
storage.strategy_counter = storage.strategy_counter + 1;

sui::event::emit(StrategySaved {
user: ctx.sender(),
strategy_id,
timestamp,
});
}

// Update existing strategy
public entry fun update_strategy(
storage: &mut UserDataStorage,
strategy_id: u64,
name: String,
description: String,
strategy_json: String,
ctx: &mut TxContext
) {
assert!(storage.owner == ctx.sender(), ENotOwner);
assert!(storage.saved_strategies.contains(strategy_id), EStrategyNotFound);

let strategy = &mut storage.saved_strategies[strategy_id];
strategy.name = name;
strategy.description = description;
strategy.strategy_json = strategy_json;
strategy.updated_at = ctx.epoch_timestamp_ms();
}

// Delete a strategy
public entry fun delete_strategy(
storage: &mut UserDataStorage,
strategy_id: u64,
ctx: &mut TxContext
) {
assert!(storage.owner == ctx.sender(), ENotOwner);
assert!(storage.saved_strategies.contains(strategy_id), EStrategyNotFound);

storage.saved_strategies.remove(strategy_id);

sui::event::emit(StrategyDeleted {
user: ctx.sender(),
strategy_id,
});
}

// Record a workflow execution
public entry fun record_execution(
storage: &mut UserDataStorage,
workflow_name: String,
workflow_id: String,
status: String,
tx_digest: String,
gas_used: u64,
result_data: String,
ctx: &mut TxContext
) {
assert!(storage.owner == ctx.sender(), ENotOwner);

let timestamp = ctx.epoch_timestamp_ms();
let execution_id = storage.execution_counter;

let entry = ExecutionEntry {
id: execution_id,
workflow_name,
workflow_id,
status,
tx_digest,
timestamp,
gas_used,
result_data,
};

storage.execution_history.add(execution_id, entry);
storage.execution_counter = storage.execution_counter + 1;

sui::event::emit(ExecutionRecorded {
user: ctx.sender(),
execution_id,
timestamp,
});
}

// Clear execution history
public entry fun clear_execution_history(
storage: &mut UserDataStorage,
ctx: &mut TxContext
) {
assert!(storage.owner == ctx.sender(), ENotOwner);

// Remove all entries from the table
let mut i = 0;
while (i < storage.execution_counter) {
if (storage.execution_history.contains(i)) {
storage.execution_history.remove(i);
};
i = i + 1;
};
storage.execution_counter = 0;

sui::event::emit(ExecutionHistoryCleared {
user: ctx.sender(),
});
}

// View functions
public fun get_strategy_count(storage: &UserDataStorage): u64 {
storage.strategy_counter
}

public fun get_execution_count(storage: &UserDataStorage): u64 {
storage.execution_counter
}

public fun has_strategy(storage: &UserDataStorage, strategy_id: u64): bool {
storage.saved_strategies.contains(strategy_id)
}

public fun has_execution(storage: &UserDataStorage, execution_id: u64): bool {
storage.execution_history.contains(execution_id)
}

// Get strategy by ID (read-only access)
public fun get_strategy(storage: &UserDataStorage, strategy_id: u64): &SavedStrategy {
assert!(storage.saved_strategies.contains(strategy_id), EStrategyNotFound);
&storage.saved_strategies[strategy_id]
}

// Get execution by ID (read-only access)
public fun get_execution(storage: &UserDataStorage, execution_id: u64): &ExecutionEntry {
assert!(storage.execution_history.contains(execution_id), EHistoryNotFound);
&storage.execution_history[execution_id]
}
}
6 changes: 5 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,16 @@
"@cetusprotocol/cetus-sui-clmm-sdk": "^4.1.0",
"@mysten/seal": "^0.9.4",
"@mysten/sui": "^1.45.0",
"@types/swagger-jsdoc": "^6.0.4",
"@types/swagger-ui-express": "^4.1.8",
"body-parser": "^2.2.0",
"cors": "^2.8.5",
"dotenv": "^17.2.3",
"express": "^5.1.0",
"multer": "^2.0.2",
"navi-sdk": "^1.2.2",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1",
"tsx": "^4.20.6",
"uuid": "^11.0.3",
"zod": "^3.23.8"
Expand All @@ -42,9 +46,9 @@
"@types/body-parser": "^1.19.6",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.5",
"@types/multer": "^1.4.11",
"@types/node": "^22.10.1",
"@types/uuid": "^10.0.0",
"@types/multer": "^1.4.11",
"@typescript-eslint/eslint-plugin": "^8.15.0",
"@typescript-eslint/parser": "^8.15.0",
"eslint": "^9.16.0",
Expand Down
Loading
Loading