diff --git a/img/architecture.png b/img/architecture.png
new file mode 100644
index 0000000..f8927f3
Binary files /dev/null and b/img/architecture.png differ
diff --git a/readme.md b/readme.md
index 9ba2f00..406a041 100644
--- a/readme.md
+++ b/readme.md
@@ -1,51 +1,46 @@
# Karst
-Karst is a web3 social graph on Starknet. it aims to build a social infrastructure for the starknet ecosystem.
-
-## TODOS
-
-- [ ] Implement `create profiles contract` functionality using `erc6551`
- - [x] Implement `create profile` functionality
- - [x] Implement `setProfileMetadataURI` functionality
- - [x] Write test for `createProfile` and related `profile` functions
-- [ ] implement `Publications` contract
-- [ ] Make contract upgradable, preferably uups.
- - [ ] implement `post` functionality
- - [ ] Implement `like` functionality
- - [ ] Implement `comment` functionality
- - [ ] Implement `mirror` functionality
- - [ ] Implement `quote` functionality
- - [ ] implement `tipPost` functionality
- - [ ] implement `follow` functionality from followNFT
-- [ ] Implement `FollowNFT` contract
- - [ ] implement `unwrap` functionality
- - [ ] Implement `approveFollow` functionality
- - [ ] Implement `removeFollower` functionality
- - [ ] Implement `wrap` functionality
- - [ ] Implement `follow` functionality
- - [ ] Implement `unfollow` functionality
- - [ ] Implement `getOriginalFollowTimestamp` functionality
- - [ ] Implement `getFollowTimestamp` functionality
- - [ ] Implement `getProfileIdAllowedToRecover` functionality
- - [ ] Implement `getFollowData` functionality
- - [ ] Implement `getFollowApproved` functionality
- - [ ] Implement `getFollowerCount` functionality
-- Implement `addDelegate` functionality
-- [ ] Implement indexing of publish contract
- - [ ] indexing shall be done with [arweave](https://www.arweave.org/)
- - [ ] index all events emitted by the publications contract
-- [ ] set up api endpoints to query the indexer
-- [ ] not important at the moment
- - [ ] create a custom explorer for querying the content layer
-
-## Remarks
-
-our implementation may defer from lens by they both achieve the same goal
-link to [Lens protocol](https://polygonscan.com/address/0x176c2a1c54e8b028eeec14bf0a059e354408ff47#code) contracts
+Karst is a permissionless and composable social graph built on Starknet, empowering creators to own every part of their social experience.
+
+With Karst, creators no longer need to worry about losing their content, audience, and livelihood based on the whims of an individual platform's algorithms and policies.
+
+## Development Setup
+You will need to have Scarb and Starknet Foundry installed on your system. Refer to the documentations below:
+
+- [Starknet Foundry](https://foundry-rs.github.io/starknet-foundry/index.html)
+- [Scarb](https://docs.swmansion.com/scarb/download.html)
+
+To use this repository, first clone it:
+```
+git clone git@github.com:horuslabsio/karst-core.git
+cd karst-core
+```
+
+### Building contracts
+To build the contracts, run the command:
+```
+scarb build
+```
+
+### Running Tests
+To run the tests contained within the `tests` folder, run the command:
+```
+snforge test
+```
+
+### Formatting contracts
+We use the in-built formatter that comes with Scarb. To format your contracts, simply run the command:
+```
+scarb fmt
+```
+
+For more information on writing and running tests, refer to the [Starknet-Foundry documentation](https://foundry-rs.github.io/starknet-foundry/index.html)
## Architecture
Check out the architecture below, and also reference [lens protocol](https://github.com/lens-protocol/core/tree/master) to understand more.
*Architecture Preview.*
-![Local Image](./img/karst%20archieture.png)
+
+
+
diff --git a/src/interfaces.cairo b/src/interfaces.cairo
index 2ed27ce..8ac959f 100644
--- a/src/interfaces.cairo
+++ b/src/interfaces.cairo
@@ -4,3 +4,4 @@ pub mod IRegistry;
pub mod IProfile;
pub mod IFollowNFT;
pub mod IPublication;
+pub mod IHandle;
diff --git a/src/interfaces/IHandle.cairo b/src/interfaces/IHandle.cairo
new file mode 100644
index 0000000..e4afc89
--- /dev/null
+++ b/src/interfaces/IHandle.cairo
@@ -0,0 +1,24 @@
+use starknet::ContractAddress;
+
+// *************************************************************************
+// INTERFACE of FollowNFT
+// *************************************************************************
+#[starknet::interface]
+pub trait IHandle {
+ // *************************************************************************
+ // EXTERNALS
+ // *************************************************************************
+ fn mint_handle(ref self: TState, address: ContractAddress, local_name: felt252) -> u256;
+ fn burn_handle(ref self: TState, token_id: u256);
+ fn set_handle_token_uri(ref self: TState, token_id: u256, local_name: felt252);
+ fn migrate_handle(ref self: TState, address: ContractAddress, local_name: felt252) -> u256;
+ // *************************************************************************
+ // GETTERS
+ // *************************************************************************
+ fn get_namespace(self: @TState) -> felt252;
+ fn get_local_name(self: @TState, token_id: u256) -> felt252;
+ fn get_handle(self: @TState, token_id: u256) -> ByteArray;
+ fn exists(self: @TState, token_id: u256) -> bool;
+ fn total_supply(self: @TState) -> u256;
+ fn get_handle_token_uri(self: @TState, token_id: u256, local_name: felt252) -> ByteArray;
+}
diff --git a/src/lib.cairo b/src/lib.cairo
index e2460a0..9971335 100644
--- a/src/lib.cairo
+++ b/src/lib.cairo
@@ -6,3 +6,5 @@ pub mod follownft;
pub mod mocks;
pub mod publication;
+
+pub mod namespaces;
diff --git a/src/namespaces.cairo b/src/namespaces.cairo
new file mode 100644
index 0000000..f3d58ad
--- /dev/null
+++ b/src/namespaces.cairo
@@ -0,0 +1,2 @@
+mod handles;
+mod handle_registry;
diff --git a/src/namespaces/handle_registry.cairo b/src/namespaces/handle_registry.cairo
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/namespaces/handle_registry.cairo
@@ -0,0 +1 @@
+
diff --git a/src/namespaces/handles.cairo b/src/namespaces/handles.cairo
new file mode 100644
index 0000000..91aaf97
--- /dev/null
+++ b/src/namespaces/handles.cairo
@@ -0,0 +1,201 @@
+// *************************************************************************
+// OZ ERC721
+// *************************************************************************
+use openzeppelin::{
+ token::erc721::{ERC721Component::{ERC721Metadata, HasComponent}},
+ introspection::src5::SRC5Component,
+};
+
+#[starknet::interface]
+trait IERC721Metadata {
+ fn name(self: @TState) -> ByteArray;
+ fn symbol(self: @TState) -> ByteArray;
+}
+
+#[starknet::embeddable]
+impl IERC721MetadataImpl<
+ TContractState,
+ +HasComponent,
+ +SRC5Component::HasComponent,
+ +Drop
+> of IERC721Metadata {
+ fn name(self: @TContractState) -> ByteArray {
+ let component = HasComponent::get_component(self);
+ ERC721Metadata::name(component)
+ }
+
+ fn symbol(self: @TContractState) -> ByteArray {
+ let component = HasComponent::get_component(self);
+ ERC721Metadata::symbol(component)
+ }
+}
+
+
+#[starknet::contract]
+mod Handles {
+ // *************************************************************************
+ // IMPORT
+ // *************************************************************************
+ use core::traits::TryInto;
+ use starknet::{ContractAddress, get_caller_address};
+ use openzeppelin::{
+ account, access::ownable::OwnableComponent,
+ token::erc721::{
+ ERC721Component, erc721::ERC721Component::InternalTrait as ERC721InternalTrait
+ },
+ introspection::{src5::SRC5Component}
+ };
+ use karst::interfaces::IHandle::IHandle;
+
+ component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
+ component!(path: SRC5Component, storage: src5, event: SRC5Event);
+ component!(path: ERC721Component, storage: erc721, event: ERC721Event);
+
+ // allow to check what interface is supported
+ #[abi(embed_v0)]
+ impl SRC5Impl = SRC5Component::SRC5Impl;
+ impl SRC5InternalImpl = SRC5Component::InternalImpl;
+
+ #[abi(embed_v0)]
+ impl ERC721Impl = ERC721Component::ERC721Impl;
+ #[abi(embed_v0)]
+ impl ERC721CamelOnlyImpl = ERC721Component::ERC721CamelOnlyImpl;
+ #[abi(embed_v0)]
+ impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl;
+
+ // add an owner
+ #[abi(embed_v0)]
+ impl OwnableImpl = OwnableComponent::OwnableImpl;
+ impl OwnableInternalImpl = OwnableComponent::InternalImpl;
+
+ // *************************************************************************
+ // STORAGE
+ // *************************************************************************
+ #[storage]
+ struct Storage {
+ #[substorage(v0)]
+ erc721: ERC721Component::Storage,
+ #[substorage(v0)]
+ src5: SRC5Component::Storage,
+ #[substorage(v0)]
+ ownable: OwnableComponent::Storage,
+ admin: ContractAddress,
+ total_supply: u256,
+ local_names: LegacyMap::,
+ karst_hub: ContractAddress,
+ }
+
+ // *************************************************************************
+ // CONSTANTS
+ // *************************************************************************
+ const MAX_LOCAL_NAME_LENGTH: u256 = 26;
+ const NAMESPACE: felt252 = 'karst';
+
+ // *************************************************************************
+ // EVENTS
+ // *************************************************************************
+ #[event]
+ #[derive(Drop, starknet::Event)]
+ enum Event {
+ #[flat]
+ ERC721Event: ERC721Component::Event,
+ #[flat]
+ SRC5Event: SRC5Component::Event,
+ #[flat]
+ OwnableEvent: OwnableComponent::Event,
+ }
+
+ // *************************************************************************
+ // CONSTRUCTOR
+ // *************************************************************************
+ #[constructor]
+ fn constructor(
+ ref self: ContractState,
+ admin: ContractAddress, // to perform upgrade
+ name: ByteArray,
+ symbol: ByteArray,
+ hub_address: ContractAddress
+ ) {
+ self.admin.write(admin);
+ self.karst_hub.write(hub_address);
+ self.erc721.initializer("KARST HANDLES", "KARST", "");
+ }
+
+ // *************************************************************************
+ // EXTERNAL FUNCTIONS
+ // *************************************************************************
+ #[abi(embed_v0)]
+ impl HandlesImpl of IHandle {
+ fn mint_handle(
+ ref self: ContractState, address: ContractAddress, local_name: felt252
+ ) -> u256 {
+ // TODO
+ return 123;
+ }
+
+ fn burn_handle(ref self: ContractState, token_id: u256) { // TODO
+ }
+
+ fn set_handle_token_uri(
+ ref self: ContractState, token_id: u256, local_name: felt252
+ ) { // TODO
+ }
+
+ fn migrate_handle(
+ ref self: ContractState, address: ContractAddress, local_name: felt252
+ ) -> u256 {
+ // TODO
+ return 123;
+ }
+
+ // *************************************************************************
+ // GETTERS
+ // *************************************************************************
+ fn get_namespace(self: @ContractState) -> felt252 {
+ return NAMESPACE;
+ }
+
+ fn get_local_name(self: @ContractState, token_id: u256) -> felt252 {
+ self.local_names.read(token_id)
+ }
+
+ fn get_handle(self: @ContractState, token_id: u256) -> ByteArray {
+ // TODO
+ return "TODO";
+ }
+
+ fn exists(self: @ContractState, token_id: u256) -> bool {
+ self.erc721._exists(token_id)
+ }
+
+ fn total_supply(self: @ContractState) -> u256 {
+ self.total_supply.read()
+ }
+
+ fn get_handle_token_uri(
+ self: @ContractState, token_id: u256, local_name: felt252
+ ) -> ByteArray {
+ // TODO
+ return "TODO";
+ }
+ }
+
+ // *************************************************************************
+ // PRIVATE FUNCTIONS
+ // *************************************************************************
+ #[generate_trait]
+ impl Private of PrivateTrait {
+ fn _mint_handle(address: ContractAddress, local_name: felt252) -> u256 {
+ // TODO
+ return 123;
+ }
+
+ fn _validate_local_name(local_name: felt252) { // TODO
+ }
+
+ fn _is_alpha_numeric(char: felt252) -> bool {
+ // TODO
+ return false;
+ }
+ }
+}