This document provides comprehensive documentation of all public contract functions in the Dongle smart contract. Each function includes its purpose, parameters, return values, authorization requirements, and possible errors.
Contract: DongleContract (Soroban/Rust)
Network: Stellar
Language: Rust
All single-entity lookup functions (such as get_project, get_collection, get_verification, get_verification_record, get_renewal_request, get_assigned_admin, get_review, get_duplicate_dispute, get_proposal, get_action, etc.) return Option<T>.
- Returns
Some(entity)when the record exists. - Returns
Nonewhen no entity is found for the given ID/key (without raising a contract error). - Multi-entity/list functions return
Vec<T>(empty when no entries match).
- Initialization & Admin Management
- Project Registry
- Project Ownership & Claiming
- Project Dependencies
- Featured Registry
- Review Registry
- Verification Registry
- Verification Renewal
- Fee Manager
- Reporting & Moderation
- Collections
- Admin Action Log
- Dispute Resolution
- TTL Management
- Contract Configuration
The admin approval threshold determines whether supported administrative actions use a direct call or an on-chain proposal:
| Current threshold | Operational path |
|---|---|
1 |
One authenticated admin may use the direct function. A proposal also works, but is immediately Approved because the proposer supplies the first approval. |
Greater than 1 |
Use create_proposal -> approve_proposal -> execute_proposal. The corresponding direct functions return Unauthorized. |
This routing applies to the actions represented by ProposalPayload: adding or removing an admin, changing the fee configuration and treasury, changing the approval threshold, and approving, rejecting, or revoking a verification. Other admin-only functions that have no ProposalPayload variant continue to use their documented direct-call authorization rules.
- Read
get_admin_listandget_admin_approval_thresholdso operators know the current eligible signers and quorum. - Construct exactly one
ProposalPayloadaction and have a current admin callcreate_proposal. The contract authenticates the proposer, assigns the next proposal ID, records the payload and its hash, and automatically adds the proposer as the first approval. The initial status isApprovedwhen that one approval meets the current threshold; otherwise it isPending. - Distribute the proposal ID and verify the stored payload with
get_proposalbefore signing. Each additional current admin callsapprove_proposalonce. Duplicate approvals fail, and approvals can only be added while the proposal isPending. The call that reaches the current threshold changes its status toApproved. - Re-read the proposal and the current threshold immediately before execution. Any current admin may call
execute_proposal; the executor does not have to be the proposer or one of the approvers. Execution checks the live threshold again, applies the payload atomically, and changes the status toExecuted. - Confirm the resulting contract state and the proposal's
Executedstatus. A proposal cannot be executed twice.
Proposals do not execute automatically when quorum is reached. There is also no proposal expiry or cancellation operation in this interface, so operational tooling should track all non-executed proposals and avoid creating ambiguous duplicates.
The threshold is not snapshotted into an AdminProposal. Creation, approval, and execution each read the threshold that is current at the time of that call. Consequently:
- Raising the threshold affects every unexecuted proposal. A proposal already marked
Approvedcan fail execution when its recorded approval count is below the new threshold. Becauseapprove_proposalonly acceptsPendingproposals, no more approvals can be added to that already-Approvedproposal; it remains blocked until the threshold is lowered sufficiently. Operators should therefore execute ready proposals before raising the threshold, or recreate them after the change. - Lowering the threshold also affects every unexecuted proposal immediately. A
Pendingproposal whose existing approval count meets the new threshold can be executed even if its stored status has not yet changed toApproved, becauseexecute_proposalvalidates the live approval count rather than requiring theApprovedstatus. A later valid approval would also refresh aPendingproposal toApproved. - Changing the threshold from a value greater than
1must itself use aSetThresholdproposal. The directset_admin_approval_thresholdcall is available only while the current threshold is1. - A proposed threshold is validated when executed and must be between
1and the admin count at that moment. Admin-set changes can therefore make a previously validSetThresholdpayload fail at execution.
Approval entries are historical addresses stored on the proposal. Execution checks their count, but does not revalidate that every approver is still an admin; only the executor must be a current admin. For predictable governance, complete or replace outstanding proposals as part of any admin rotation.
let payload = ProposalPayload::SetThreshold(3);
// admin_1's signature is recorded as approval one.
let proposal_id = create_proposal(env, admin_1, payload)?;
// A distinct current admin supplies approval two, reaching the current 2-of-3 quorum.
approve_proposal(env, admin_2, proposal_id)?;
// Any current admin may execute. This changes the threshold to 3.
execute_proposal(env, admin_3, proposal_id)?;The address arguments shown above must authorize their respective contract invocations.
Purpose: Initialize the contract with an initial admin address. This function must be called exactly once before any other operations.
Parameters:
env(Env): The contract environmentadmin(Address): The initial admin address
Return Value: None (void)
Authorization:
- Any address can call this during initialization (typically the contract deployer)
- Only callable once; subsequent calls will fail
Possible Errors:
- None (initialization is guarded internally)
Example:
initialize(env, admin_address);Purpose: Add a new admin address to the contract (admin-only operation).
Parameters:
env(Env): The contract environmentcaller(Address): The admin calling this function (must be an existing admin)new_admin(Address): The address to promote to admin
Return Value: Result<(), ContractError>
- Success:
Ok(()) - Failure:
ContractError
Authorization:
- Caller must be an existing admin (
is_admin(env, caller)must return true)
Possible Errors:
AdminOnly- Caller is not an adminAdminNotFound- Caller address not found in admin list
Example:
add_admin(env, admin_address, new_admin_address)?;Purpose: Remove an admin address from the contract (admin-only operation).
Parameters:
env(Env): The contract environmentcaller(Address): The admin calling this functionadmin_to_remove(Address): The admin address to remove
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an existing admin
Possible Errors:
AdminOnly- Caller is not an adminCannotRemoveLastAdmin- Cannot remove the last admin (contract must maintain at least one admin)AdminNotFound- Admin to remove not found
Example:
remove_admin(env, caller, admin_to_remove)?;Purpose: Check if an address is an admin.
Parameters:
env(Env): The contract environmentaddress(Address): The address to check
Return Value: bool
trueif the address is an adminfalseotherwise
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let is_admin_flag = is_admin(env, some_address);Purpose: Retrieve the complete list of all admin addresses.
Parameters:
env(Env): The contract environment
Return Value: Vec<Address>
- A vector containing all admin addresses
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let admins = get_admin_list(env);Purpose: Get the total number of admins in the contract.
Parameters:
env(Env): The contract environment
Return Value: u32
- The count of admins
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let admin_count = get_admin_count(env);Purpose: Return a stable public contract configuration snapshot for frontends and indexers.
Parameters:
env(Env): The contract environment
Return Value: ContractConfig
fee_config: current fee configuration when settreasury: current treasury address when setadmin_count: current admin countpaused: current pause state; currentlyfalsebecause no pause feature is implementedversion: contract config version string- public limits for projects, reviews, pagination, tags, social links, verification validity, fee payment expiry, and review update cooldown
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let config = get_config(env);Purpose: Register a new project on-chain with metadata.
Parameters:
env(Env): The contract environmentparams(ProjectRegistrationParams): Registration parameters containing:owner(Address): The owner/creator of the projectname(String): Project name (max length enforced)slug(String): URL-friendly project identifier (must be unique)description(String): Project description (max length enforced)category(String): Project category (max length enforced)website(Option): Optional project website URLlogo_cid(Option): Optional IPFS CID for project logometadata_cid(Option): Optional IPFS CID for extended metadatatags(Option<Vec>): Optional tags (max 10 tags, validated)social_links(Option<Map<String, String>>): Optional social media links (max 10, validated)launch_timestamp(Option): Optional Unix timestamp of project launch
Return Value: Result<u64, ContractError>
- Success:
Ok(project_id)- The unique ID of the registered project - Failure:
ContractError
Authorization:
- None (permissionless) - Any address can register a project
Possible Errors:
ProjectAlreadyExists- A project with the same slug already existsInvalidProjectName- Project name format is invalidProjectNameTooLong- Project name exceeds maximum lengthInvalidProjectDesc- Project description format is invalidProjectDescTooLong- Project description exceeds maximum lengthInvalidCategory- Category format is invalidCategoryTooLong- Category exceeds maximum lengthInvalidWebsite- Website URL format is invalidWebsiteTooLong- Website URL exceeds maximum lengthInvalidLogoCid- Logo CID format is invalidInvalidMetaCid- Metadata CID format is invalidInvalidTag- Tag format is invalidTooManyTags- More than 10 tags providedInvalidSocialLink- Social link format is invalidTooManySocialLinks- More than 10 social links providedMaxProjectsExceeded- Contract has reached maximum project capacity
Example:
let project_id = register_project(env, ProjectRegistrationParams {
owner: owner_address,
name: String::from_slice(&env, "My Project"),
slug: String::from_slice(&env, "my-project"),
description: String::from_slice(&env, "A great project"),
category: String::from_slice(&env, "DeFi"),
website: Some(String::from_slice(&env, "https://example.com")),
logo_cid: Some(String::from_slice(&env, "QmXxxx...")),
metadata_cid: None,
tags: Some(vec![&env, String::from_slice(&env, "defi")]),
social_links: None,
launch_timestamp: None,
})?;Purpose: Update project metadata (owner-only).
Parameters:
env(Env): The contract environmentparams(ProjectUpdateParams): Update parameters containing:project_id(u64): The ID of the project to updatecaller(Address): The address performing the update (must be project owner)name(Option): Optional new project nameslug(Option): Optional new slugdescription(Option): Optional new descriptioncategory(Option): Optional new categorywebsite(Option<Option>): Optional new website URL (or None to remove)logo_cid(Option<Option>): Optional new logo CIDmetadata_cid(Option<Option>): Optional new metadata CIDtags(Option<Option<Vec>>): Optional new tagssocial_links(Option<Option<Map<String, String>>>): Optional new social linkslaunch_timestamp(Option<Option>): Optional new launch timestamp
Return Value: Result<Project, ContractError>
- Success:
Ok(updated_project)- The updated project data - Failure:
ContractError
Authorization:
- Caller must be the project owner
Possible Errors:
ProjectNotFound- Project ID does not existUnauthorized- Caller is not the project ownerProjectAlreadyExists- New slug conflicts with existing projectInvalidProjectName- Invalid name formatProjectNameTooLong- Name exceeds maximum lengthInvalidProjectDesc- Invalid description formatProjectDescTooLong- Description exceeds maximum lengthInvalidCategory- Invalid category formatCategoryTooLong- Category exceeds maximum lengthInvalidWebsite- Invalid website URLWebsiteTooLong- Website exceeds maximum lengthInvalidLogoCid- Invalid logo CID formatInvalidMetaCid- Invalid metadata CID formatInvalidTag- Invalid tag formatTooManyTags- More than 10 tagsInvalidSocialLink- Invalid social link formatTooManySocialLinks- More than 10 social links
Example:
let updated_project = update_project(env, ProjectUpdateParams {
project_id: 1,
caller: owner_address,
name: Some(String::from_slice(&env, "Updated Project Name")),
slug: None,
description: None,
category: None,
website: None,
logo_cid: None,
metadata_cid: None,
tags: None,
social_links: None,
launch_timestamp: None,
})?;Purpose: Update the security contact for a project (owner-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDcaller(Address): The project ownercontact(Option): Optional security contact email/identifier
Return Value: Result<Project, ContractError>
- Success:
Ok(updated_project)- The updated project with security contact - Failure:
ContractError
Authorization:
- Caller must be the project owner
Possible Errors:
ProjectNotFound- Project ID does not existUnauthorized- Caller is not the project owner
Example:
update_security_contact(env, project_id, owner_address, Some(String::from_slice(&env, "security@example.com")))?;Purpose: Submit proof of security contact ownership via IPFS CID (owner-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDcaller(Address): The project ownerproof_cid(String): IPFS CID containing proof of security contact
Return Value: Result<Project, ContractError>
Authorization:
- Caller must be the project owner
Possible Errors:
ProjectNotFound- Project ID does not existUnauthorized- Caller is not the project owner
Example:
submit_security_contact_proof(env, project_id, owner_address, String::from_slice(&env, "QmProof..."))?;Purpose: Get the security contact verification status for a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: Result<SecurityContactStatus, ContractError>
- Contains the current security contact and verification status
Authorization:
- None (read-only, permissionless)
Possible Errors:
ProjectNotFound- Project ID does not exist
Example:
let status = get_security_contact_status(env, project_id)?;Purpose: Retrieve a single project by ID.
Parameters:
env(Env): The contract environmentproject_id(u64): The ID of the project to retrieve
Return Value: Option<Project>
Some(project)if foundNoneif not found
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
if let Some(project) = get_project(env, 1) {
// Use project data
}Purpose: Retrieve a project by its slug (URL-friendly identifier).
Parameters:
env(Env): The contract environmentslug(String): The project slug
Return Value: Option<Project>
Some(project)if foundNoneif not found
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
if let Some(project) = get_project_by_slug(env, String::from_slice(&env, "my-project")) {
// Use project data
}Purpose: Retrieve projects with pagination, sorted by project ID.
Parameters:
env(Env): The contract environmentstart_id(u64): The starting project ID for paginationlimit(u32): Maximum number of projects to return
Return Value: Vec<Project>
- A vector of projects matching the criteria
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let projects = list_projects(env, 0, 10); // Get first 10 projectsPurpose: Retrieve all projects owned by a specific address.
Parameters:
env(Env): The contract environmentowner(Address): The owner address
Return Value: Vec<Project>
- A vector of all projects owned by the address
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let my_projects = get_projects_by_owner(env, owner_address);Purpose: Get the count of projects owned by an address.
Parameters:
env(Env): The contract environmentowner(Address): The owner address
Return Value: u32
- The number of projects owned by the address
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let count = get_owner_project_count(env, owner_address);Purpose: Get the total number of projects in the contract.
Parameters:
env(Env): The contract environment
Return Value: u64
- The total count of projects
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let total = get_project_count(env);Purpose: Retrieve multiple projects by a list of IDs.
Parameters:
env(Env): The contract environmentids(Vec): A vector of project IDs
Return Value: Vec<Project>
- A vector of projects found (missing IDs are skipped)
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let projects = get_projects_by_ids(env, vec![&env, 1, 2, 3]);Purpose: Set or remove an optional region tag for a project (owner-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDcaller(Address): The project ownerregion(Option): Optional region tag (e.g., "US", "EU")
Return Value: Result<(), ContractError>
Authorization:
- Caller must be the project owner
Possible Errors:
ProjectNotFound- Project ID does not existUnauthorized- Caller is not the project owner
Example:
set_project_region(env, project_id, owner_address, Some(String::from_slice(&env, "EU")))?;Purpose: Get the region tag for a project, if set.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: Option<String>
Some(region)if a region tag is setNoneif no region tag
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
if let Some(region) = get_project_region(env, project_id) {
// Use region data
}Purpose: Get the stored integrity hash for a project, if any.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: Option<Bytes>
Some(hash)if an integrity hash is storedNoneif no hash
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
if let Some(hash) = get_project_integrity_hash(env, project_id) {
// Verify project integrity
}Purpose: Retrieve projects sorted by a specified sort mode with pagination.
Parameters:
env(Env): The contract environmentsort_mode(ProjectSortMode): The sorting mode (e.g., by rating, by name)start_index(u64): Zero-based index into the sorted result for paginationlimit(u32): Maximum number of projects to return
Return Value: Vec<Project>
- A vector of projects sorted by the specified mode
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let sorted_projects = list_projects_sorted(env, ProjectSortMode::Rating, 0, 20);Purpose: Retrieve projects filtered by verification status with pagination.
Parameters:
env(Env): The contract environmentstatus(VerificationStatus): The verification status to filter by (Unverified, Pending, Verified, Rejected)start_id(u64): The starting project ID for paginationlimit(u32): Maximum number of projects to return
Return Value: Vec<Project>
- A vector of projects with the specified status
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let verified_projects = list_projects_by_status(env, VerificationStatus::Verified, 0, 20);Purpose: Retrieve projects filtered by category with pagination.
Parameters:
env(Env): The contract environmentcategory(String): The category to filter bystart_index(u32): Zero-based index into the category's project ID list for paginationlimit(u32): Maximum number of projects to return
Return Value: Vec<Project>
- A vector of projects in the specified category
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let defi_projects = list_projects_by_category(env, String::from_slice(&env, "DeFi"), 0, 10);Purpose: Retrieve projects filtered by tag with pagination.
Parameters:
env(Env): The contract environmenttag(String): The tag to filter bystart_index(u32): Zero-based offset into the project ID scan space for paginationlimit(u32): Maximum number of projects to return
Return Value: Vec<Project>
- A vector of projects with the specified tag
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let tagged_projects = list_projects_by_tag(env, String::from_slice(&env, "nft"), 0, 10);Purpose: Archive a project (owner or admin can archive, prevents further reviews/verification).
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID to archivecaller(Address): The address performing the archive
Return Value: Result<(), ContractError>
Authorization:
- Caller must be project owner or admin
Possible Errors:
ProjectNotFound- Project ID does not existUnauthorized- Caller is neither owner nor adminAlreadyArchived- Project is already archived
Example:
archive_project(env, project_id, owner_address)?;Purpose: Reactivate an archived project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID to reactivatecaller(Address): The address performing the reactivation
Return Value: Result<(), ContractError>
Authorization:
- Caller must be project owner or admin
Possible Errors:
ProjectNotFound- Project ID does not existUnauthorized- Caller is neither owner nor adminProjectNotArchived- Project is not archived
Example:
reactivate_project(env, project_id, owner_address)?;Purpose: Add a maintainer to a project (owner-only). Maintainers can assist with project management.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDcaller(Address): The project ownermaintainer(Address): The address to add as maintainer
Return Value: Result<(), ContractError>
Authorization:
- Caller must be the project owner
Possible Errors:
ProjectNotFound- Project ID does not existUnauthorized- Caller is not the project owner
Example:
add_maintainer(env, project_id, owner_address, maintainer_address)?;Purpose: Remove a maintainer from a project (owner-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDcaller(Address): The project ownermaintainer(Address): The maintainer address to remove
Return Value: Result<(), ContractError>
Authorization:
- Caller must be the project owner
Possible Errors:
ProjectNotFound- Project ID does not existUnauthorized- Caller is not the project owner
Example:
remove_maintainer(env, project_id, owner_address, maintainer_address)?;Purpose: Get the list of maintainers for a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: Vec<Address>
- A vector of maintainer addresses
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let maintainers = get_maintainers(env, project_id);Purpose: Add a name to the reserved project names list (admin-only).
Parameters:
env(Env): The contract environmentadmin(Address): The admin callingname(String): The name to reserve
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an admin
Example:
add_reserved_name(env, admin_address, String::from_slice(&env, "reserved-name"))?;Purpose: Remove a name from the reserved list (admin-only).
Parameters:
env(Env): The contract environmentadmin(Address): The admin callingname(String): The name to unreserve
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an admin
Example:
remove_reserved_name(env, admin_address, String::from_slice(&env, "reserved-name"))?;Purpose: Get the list of all reserved project names.
Parameters:
env(Env): The contract environment
Return Value: Vec<String>
- A vector of reserved names
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let reserved = get_reserved_names(env);Purpose: Check if a specific name is reserved.
Parameters:
env(Env): The contract environmentname(String): The name to check
Return Value: bool
trueif the name is reservedfalseotherwise
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let reserved = is_name_reserved(env, String::from_slice(&env, "some-name"));Purpose: Link two projects together (owner-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The primary project IDcaller(Address): The project ownerlinked_project_id(u64): The project ID to link
Return Value: Result<(), ContractError>
Authorization:
- Caller must be the owner of the primary project
Possible Errors:
ProjectNotFound- One or both project IDs do not existUnauthorized- Caller is not the project ownerCannotLinkToSelf- Cannot link a project to itselfAlreadyLinked- Projects are already linked
Example:
link_project(env, 1, owner_address, 2)?;Purpose: Unlink two projects (owner-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The primary project IDcaller(Address): The project ownerlinked_project_id(u64): The project ID to unlink
Return Value: Result<(), ContractError>
Authorization:
- Caller must be the owner of the primary project
Possible Errors:
ProjectNotFound- One or both project IDs do not existUnauthorized- Caller is not the project ownerCannotLinkToSelf- Cannot unlink a project from itself
Example:
unlink_project(env, 1, owner_address, 2)?;Purpose: Get all projects linked to a specific project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: Vec<u64>
- A vector of linked project IDs
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let linked_ids = get_linked_projects(env, 1);Purpose: Initiate a project ownership transfer (requires approval from new owner).
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID to transfercaller(Address): The current project ownernew_owner(Address): The address of the new owner
Return Value: Result<(), ContractError>
Authorization:
- Caller must be the current project owner
Possible Errors:
ProjectNotFound- Project ID does not existUnauthorized- Caller is not the project owner
Example:
initiate_transfer(env, project_id, owner_address, new_owner_address)?;Purpose: Cancel a pending project ownership transfer.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDcaller(Address): The current project owner
Return Value: Result<(), ContractError>
Authorization:
- Caller must be the current project owner
Possible Errors:
ProjectNotFound- Project ID does not existUnauthorized- Caller is not the project ownerTransferNotFound- No pending transfer for this project
Example:
cancel_transfer(env, project_id, owner_address)?;Purpose: Accept a project ownership transfer (new owner accepts).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDcaller(Address): The pending new owner
Return Value: Result<(), ContractError>
Authorization:
- Caller must be the pending new owner of the project
Possible Errors:
ProjectNotFound- Project ID does not existTransferNotFound- No pending transfer for this projectNotTransferRecip- Caller is not the pending new owner
Example:
accept_transfer(env, project_id, new_owner_address)?;Purpose: Claim ownership of a contract address associated with a project (owner-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDcaller(Address): The project ownercontract_address(String): The contract address to claimproof_cid(String): IPFS CID containing proof of ownership
Return Value: Result<ContractClaimRequest, ContractError>
- Success:
Ok(claim_request)- The created contract claim request - Failure:
ContractError
Authorization:
- Caller must be the project owner
Possible Errors:
ProjectNotFound- Project ID does not existUnauthorized- Caller is not the project owner
Example:
let claim = claim_contract_address(env, project_id, owner_address, String::from_slice(&env, "CC..."), String::from_slice(&env, "QmProof..."))?;Purpose: Approve a contract address claim (admin-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDcontract_address(String): The contract address being claimedadmin(Address): The admin approving
Return Value: Result<ContractClaimRequest, ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminProjectNotFound- Project ID does not exist
Example:
approve_contract_claim(env, project_id, String::from_slice(&env, "CC..."), admin_address)?;Purpose: Reject a contract address claim (admin-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDcontract_address(String): The contract address being claimedadmin(Address): The admin rejecting
Return Value: Result<ContractClaimRequest, ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminProjectNotFound- Project ID does not exist
Example:
reject_contract_claim(env, project_id, String::from_slice(&env, "CC..."), admin_address)?;Purpose: Get all verified contract addresses for a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: Vec<String>
- A vector of verified contract addresses
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let contracts = get_verified_contracts(env, project_id);Purpose: Mark a project as claimable by others (owner-only). Used when the original owner no longer maintains it.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDcaller(Address): The project ownerclaimable(bool): True to make claimable, false to revoke
Return Value: Result<(), ContractError>
Authorization:
- Caller must be the project owner
Possible Errors:
ProjectNotFound- Project ID does not existUnauthorized- Caller is not the project owner
Example:
set_project_claimable(env, project_id, owner_address, true)?;Purpose: Submit a claim request for a claimable project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID to claimclaimant(Address): The address submitting the claimproof_cid(String): IPFS CID containing proof of stewardship
Return Value: Result<u64, ContractError>
- Success:
Ok(claim_request_id)- The ID of the claim request - Failure:
ContractError
Authorization:
- Any address can submit a claim for a claimable project
Possible Errors:
ProjectNotFound- Project ID does not existInvalidProjectData- Project is not marked as claimable
Example:
let claim_id = submit_claim_request(env, project_id, claimant_address, String::from_slice(&env, "QmXxxx..."))?;Purpose: Approve a claim request (admin-only).
Parameters:
env(Env): The contract environmentclaim_request_id(u64): The claim request ID to approveadmin(Address): The admin approving the request
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminProjectNotFound- Associated project not found
Example:
approve_claim_request(env, claim_request_id, admin_address)?;Purpose: Reject a claim request (admin-only).
Parameters:
env(Env): The contract environmentclaim_request_id(u64): The claim request ID to rejectadmin(Address): The admin rejecting the request
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an admin
Example:
reject_claim_request(env, claim_request_id, admin_address)?;Purpose: Retrieve a single claim request by ID.
Parameters:
env(Env): The contract environmentclaim_request_id(u64): The claim request ID
Return Value: Option<ClaimRequest>
Some(claim_request)if foundNoneif not found
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
if let Some(claim_req) = get_claim_request(env, claim_id) {
// Use claim request data
}Purpose: Get all claim requests for a specific project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: Vec<ClaimRequest>
- A vector of all claim requests for the project
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let claims = get_claim_requests_for_project(env, project_id);Purpose: Add a dependency to a project (owner-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDcaller(Address): The project ownerdependency(ProjectDependency): The dependency to add containing:reference(DependencyRef): Reference to the dependency (project_id, external_cid, or external_url)label(Option): Optional label (e.g., "oracle", "token")metadata_cid(Option): Optional metadata CIDadded_at(u64): Unix timestamp (usually current time)updated_at(u64): Unix timestamp (usually current time)
Return Value: Result<(), ContractError>
Authorization:
- Caller must be the project owner
Possible Errors:
ProjectNotFound- Project ID does not existUnauthorized- Caller is not the project owner
Example:
add_project_dependency(env, project_id, owner_address, ProjectDependency {
reference: DependencyRef {
project_id: Some(2),
external_cid: None,
external_url: None,
},
label: Some(String::from_slice(&env, "oracle")),
metadata_cid: None,
added_at: env.ledger().timestamp(),
updated_at: env.ledger().timestamp(),
})?;Purpose: Update an existing project dependency (owner-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDcaller(Address): The project ownerdependency_key(DependencyRef): The existing dependency reference to updatenew_dependency(ProjectDependency): The updated dependency data
Return Value: Result<(), ContractError>
Authorization:
- Caller must be the project owner
Possible Errors:
ProjectNotFound- Project ID does not existUnauthorized- Caller is not the project owner
Example:
update_project_dependency(env, project_id, owner_address, old_ref, new_dependency)?;Purpose: Remove a dependency from a project (owner-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDcaller(Address): The project ownerdependency_key(DependencyRef): The dependency reference to remove
Return Value: Result<(), ContractError>
Authorization:
- Caller must be the project owner
Possible Errors:
ProjectNotFound- Project ID does not existUnauthorized- Caller is not the project owner
Example:
remove_project_dependency(env, project_id, owner_address, dependency_ref)?;Purpose: Retrieve all dependencies for a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: Vec<ProjectDependency>
- A vector of all project dependencies
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let dependencies = get_project_dependencies(env, project_id);Purpose: Set whether a project is featured (admin-only).
Parameters:
env(Env): The contract environmentadmin(Address): The admin addressproject_id(u64): The project ID to feature/unfeaturefeatured(bool): True to feature, false to unfeature
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminProjectNotFound- Project ID does not exist
Example:
set_featured(env, admin_address, project_id, true)?;Purpose: Retrieve all featured projects with pagination.
Parameters:
env(Env): The contract environmentstart(u32): The starting index for paginationlimit(u32): Maximum number of projects to return
Return Value: Vec<Project>
- A vector of featured projects
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let featured = list_featured_projects(env, 0, 20);Purpose: Add or create a review for a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID being reviewedreviewer(Address): The review authorrating(u32): The rating (typically 1-5, validated by contract)comment_cid(Option): Optional IPFS CID containing the review text
Return Value: Result<(), ContractError>
Authorization:
- Caller (reviewer) can submit review for any project (unless reviews are disabled for that project)
Possible Errors:
ProjectNotFound- Project ID does not existInvalidRating- Rating is not in valid rangeDuplicateReview- Reviewer has already reviewed this projectReviewsDisabled- Reviews are disabled for this projectProjectNotArchived- Cannot review archived projects
Example:
add_review(env, project_id, reviewer_address, 5, Some(String::from_slice(&env, "QmXxxx...")))?;Purpose: Submit a review with content CID (alternative to add_review).
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID being reviewedreviewer(Address): The review authorrating(u32): The ratingreview_cid(String): IPFS CID containing the review content
Return Value: Result<(), ContractError>
Authorization:
- Reviewer can submit review
Possible Errors:
ProjectNotFound- Project ID does not existInvalidRating- Rating is not validDuplicateReview- Reviewer has already reviewed this projectReviewsDisabled- Reviews disabled for project
Example:
submit_review(env, project_id, reviewer_address, 4, String::from_slice(&env, "QmXxxx..."))?;Purpose: Update an existing review (reviewer-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDreviewer(Address): The review authorrating(u32): The new ratingcomment_cid(Option): Optional new comment CID
Return Value: Result<(), ContractError>
Authorization:
- Caller must be the reviewer
Possible Errors:
ProjectNotFound- Project ID does not existReviewNotFound- Review does not exist for this reviewerInvalidRating- Rating is not validNotReviewOwner- Caller is not the reviewer
Example:
update_review(env, project_id, reviewer_address, 3, Some(String::from_slice(&env, "QmYyyy...")))?;Purpose: Delete a review (reviewer-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDreviewer(Address): The review author
Return Value: Result<(), ContractError>
Authorization:
- Caller must be the reviewer
Possible Errors:
ProjectNotFound- Project ID does not existReviewNotFound- Review does not existNotReviewOwner- Caller is not the reviewer
Example:
delete_review(env, project_id, reviewer_address)?;Purpose: Project owner responds to a review.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDcaller(Address): The project ownerreviewer(Address): The reviewer being responded toresponse(String): The response text
Return Value: Result<(), ContractError>
Authorization:
- Caller must be the project owner
Possible Errors:
ProjectNotFound- Project ID does not existReviewNotFound- Review does not existUnauthorized- Caller is not the project owner
Example:
respond_to_review(env, project_id, owner_address, reviewer_address, String::from_slice(&env, "Thank you for the feedback!"))?;Purpose: Get the project owner's response to a review.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDreviewer(Address): The reviewer
Return Value: Option<String>
Some(response)if a response existsNoneif no response
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
if let Some(response) = get_review_response(env, project_id, reviewer_address) {
// Use response text
}Purpose: Retrieve a specific review by project and reviewer.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDreviewer(Address): The reviewer address
Return Value: Option<Review>
Some(review)if foundNoneif not found
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
if let Some(review) = get_review(env, project_id, reviewer_address) {
// Use review data
}Purpose: Get the content CID of a review.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDreviewer(Address): The reviewer address
Return Value: Option<String>
Some(cid)if a review with content CID existsNoneotherwise
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
if let Some(cid) = get_review_cid(env, project_id, reviewer_address) {
// Fetch full review from IPFS
}Purpose: Get all review content CIDs for a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: Vec<(Address, String)>
- A vector of (reviewer_address, content_cid) pairs
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let review_cids = get_project_review_cids(env, project_id);
// Each entry is (reviewer_address, cid_string)Purpose: Retrieve multiple reviews by a list of (project_id, reviewer) pairs.
Parameters:
env(Env): The contract environmentids(Vec<(u64, Address)>): Vector of (project_id, reviewer_address) tuples
Return Value: Vec<Review>
- Vector of reviews found (missing combinations are skipped)
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let reviews = get_reviews_by_ids(env, vec![&env, (1, reviewer1), (1, reviewer2)]);Purpose: List reviews for a project with pagination.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDstart_index(u32): Zero-based index into the project's review list for paginationlimit(u32): Maximum number of reviews to return
Return Value: Vec<Review>
- A vector of reviews for the project
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let reviews = list_reviews(env, project_id, 0, 50);Purpose: Get aggregated statistics for a project (review count, average rating).
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: ProjectStats
- Contains:
rating_sum(u64): Sum of all ratingsreview_count(u32): Number of reviewsaverage_rating(u32): Average rating
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let stats = get_project_stats(env, project_id);
let avg = stats.average_rating;Purpose: Get statistics for multiple projects at once.
Parameters:
env(Env): The contract environmentids(Vec): Vector of project IDs
Return Value: Vec<(u64, ProjectStats)>
- Vector of (project_id, stats) tuples
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let batch_stats = get_stats_batch(env, vec![&env, 1, 2, 3]);Purpose: Get the Bayesian weighted rating for a project (scaled by 100).
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: u32
- The weighted rating (e.g., 450 = 4.50)
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let weighted = get_weighted_rating(env, project_id);Purpose: Get the number of revisions a review has gone through.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDreviewer(Address): The reviewer address
Return Value: u32
- The number of revisions
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let revisions = get_review_revision_count(env, project_id, reviewer_address);Purpose: Get revision history for a specific review with pagination.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDreviewer(Address): The reviewer addressstart_index(u32): Starting index for paginationlimit(u32): Maximum records to return
Return Value: Vec<ReviewRevision>
- A vector of review revisions
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let history = get_review_history(env, project_id, reviewer_address, 0, 10);Purpose: Get the deletion tombstone for a review, distinguishing deleted reviews from never-existed.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDreviewer(Address): The reviewer address
Return Value: Option<ReviewTombstone>
Some(tombstone)if the review was deletedNoneif the review never existed or was never deleted
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
if let Some(tombstone) = get_review_tombstone(env, project_id, reviewer_address) {
// Review was deleted at tombstone.timestamp
}Purpose: List reviews for a project sorted by a specified sort mode with pagination.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDstart_index(u32): Zero-based index into the project's review list for paginationlimit(u32): Maximum reviews to returnsort_mode(ReviewSortMode): The sorting mode
Return Value: Vec<Review>
- A vector of sorted reviews
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let reviews = list_reviews_sorted(env, project_id, 0, 20, ReviewSortMode::Rating);Purpose: Enable or disable reviews for a project (owner-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDcaller(Address): The project ownerenabled(bool): True to enable reviews, false to disable
Return Value: Result<(), ContractError>
Authorization:
- Caller must be the project owner
Possible Errors:
ProjectNotFound- Project ID does not existUnauthorized- Caller is not the project owner
Example:
set_reviews_enabled(env, project_id, owner_address, false)?;Purpose: Check if reviews are enabled for a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: bool
trueif reviews are enabledfalseif disabled
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let enabled = get_reviews_enabled(env, project_id);Purpose: Report a review for moderation (spam, abuse, etc.).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDreviewer(Address): The review authorreporter(Address): The address reporting the review
Return Value: Result<(), ContractError>
Authorization:
- Any address can report a review
Possible Errors:
ProjectNotFound- Project ID does not existReviewNotFound- Review does not existAlreadyReported- Caller has already reported this reviewReviewAlreadyReported- Review has already been reported
Example:
report_review(env, project_id, reviewer_address, reporter_address)?;Purpose: Hide a review from public view (admin-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDreviewer(Address): The review authoradmin(Address): The admin hiding the review
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminProjectNotFound- Project ID does not existReviewNotFound- Review does not existReviewAlreadyHidden- Review is already hidden
Example:
hide_review(env, project_id, reviewer_address, admin_address)?;Purpose: Restore a hidden review to public view (admin-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDreviewer(Address): The review authoradmin(Address): The admin restoring the review
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminProjectNotFound- Project ID does not existReviewNotFound- Review does not existReviewNotHidden- Review is not hidden
Example:
restore_review(env, project_id, reviewer_address, admin_address)?;Purpose: Permanently delete a review (admin-only, irreversible).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDreviewer(Address): The review authoradmin(Address): The admin deleting the review
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminProjectNotFound- Project ID does not existReviewNotFound- Review does not exist
Example:
admin_delete_review(env, project_id, reviewer_address, admin_address)?;Purpose: Request verification of a project (requires fee, if configured).
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID to verifyrequester(Address): The address requesting verificationevidence_cid(String): IPFS CID containing verification evidence
Return Value: Result<(), ContractError>
Authorization:
- Any address can request verification for any project
- Project owner typically submits their own projects
Possible Errors:
ProjectNotFound- Project ID does not existProjectTooYoung- Project age is below minimum required ageUnauthorized- If project is not claimable and caller is not ownerInvalidProjectData- Project data is invalid
Example:
request_verification(env, project_id, requester_address, String::from_slice(&env, "QmXxxx..."))?;Purpose: Update the verification evidence CID for a pending verification request (project owner only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDcaller(Address): The project ownernew_evidence_cid(String): The new IPFS CID containing updated evidence
Return Value: Result<(), ContractError>
Authorization:
- Caller must be the project owner
- Updates are allowed only when the request status is
Pending
Possible Errors:
ProjectNotFound- Project ID does not existUnauthorized- Caller is not the project ownerVerificationNotFound- No pending verification request
Example:
update_verification_evidence(env, project_id, owner_address, String::from_slice(&env, "QmNewEvidence..."))?;Purpose: Approve a pending verification request (admin-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDadmin(Address): The admin approving
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminProjectNotFound- Project ID does not existVerificationNotFound- No pending verification request
Example:
approve_verification(env, project_id, admin_address)?;Purpose: Reject a pending verification request (admin-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDadmin(Address): The admin rejecting
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminProjectNotFound- Project ID does not existVerificationNotFound- No pending verification request
Example:
reject_verification(env, project_id, admin_address)?;Purpose: Revoke an active verification (admin-only, typically for compliance).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDadmin(Address): The admin revokingreason(String): Reason for revocation
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminProjectNotFound- Project ID does not existVerificationNotFound- Project is not verifiedNotRevocable- Verification cannot be revoked (already revoked, etc.)
Example:
revoke_verification(env, project_id, admin_address, String::from_slice(&env, "Compliance issue"))?;Purpose: Get the current verification status of a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: Option<VerificationRecord>
Some(VerificationRecord)if found,Noneif not found.- Contains:
request_id(u64): ID of the verification requestproject_id(u64): Project IDrequester(Address): Who requested verificationstatus(VerificationStatus): Current status (Unverified, Pending, Verified, Rejected)evidence_cid(String): CID of evidencetimestamp(u64): Request timestampfee_amount(u128): Fee paidrevoke_reason(Option): Reason if revokedexpires_at(u64): Expiry timestamp (0 = no expiry)last_renewed_at(u64): Last renewal timestamp
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None (returns
Noneif verification record does not exist)
Example:
let verification = get_verification(env, project_id);Purpose: Get a verification record by its request ID.
Parameters:
env(Env): The contract environmentrequest_id(u64): The verification request ID
Return Value: Result<VerificationRecord, ContractError>
Authorization:
- None (read-only, permissionless)
Possible Errors:
VerificationNotFound- No verification record for this request ID
Example:
let record = get_verification_record(env, request_id)?;Purpose: Get verification records for multiple projects.
Parameters:
env(Env): The contract environmentids(Vec): Vector of project IDs
Return Value: Vec<(u64, VerificationRecord)>
- Vector of (project_id, verification_record) tuples
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let verifications = get_verifications_batch(env, vec![&env, 1, 2, 3]);Purpose: Get the complete verification history for a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: Vec<VerificationRecord>
- A vector of all verification records (past and present)
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let history = get_verification_history(env, project_id);Purpose: Check if a project's verification has expired.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: Result<bool, ContractError>
trueif verification has expiredfalseif not expired or no expiry configured
Authorization:
- None (read-only, permissionless)
Possible Errors:
ProjectNotFound- Project ID does not existVerificationNotFound- No verification for project
Example:
let expired = is_verification_expired(env, project_id)?;Purpose: Report whether a verification will expire within a caller-supplied renewal-warning threshold.
Parameters:
project_id(u64): Project to inspect.threshold_seconds(u64): Maximum remaining lifetime for the warning.
Returns:
truewhen the verification has a nonzero expiry, is not already expired, and has at mostthreshold_secondsremaining.falsefor no-expiry and already-expired records.
let expiring_soon = is_verification_expiring_soon(env, project_id, 2_592_000)?;Purpose: Admin: prune verification history, keeping the most recent keep_count records.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDadmin(Address): The admin performing the operationkeep_count(u32): Number of most recent records to keep
Return Value: Result<u32, ContractError>
- Success:
Ok(removed_count)- Number of records removed - Failure:
ContractError
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminProjectNotFound- Project ID does not exist
Example:
let removed = clear_verification_history(env, project_id, admin_address, 5)?;Purpose: Admin: clear all renewal history records for a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDadmin(Address): The admin performing the operation
Return Value: Result<u32, ContractError>
- Success:
Ok(removed_count)- Number of records removed - Failure:
ContractError
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminProjectNotFound- Project ID does not exist
Example:
let removed = clear_renewal_history(env, project_id, admin_address)?;Purpose: Request renewal of an expiring or expired verification.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDrequester(Address): The address requesting renewalevidence_cid(String): IPFS CID containing updated evidence
Return Value: Result<(), ContractError>
Authorization:
- Any address can request (typically project owner)
Possible Errors:
ProjectNotFound- Project ID does not existVerificationNotFound- No existing verification to renew
Example:
request_renewal(env, project_id, requester_address, String::from_slice(&env, "QmXxxx..."))?;Purpose: Approve a renewal request (admin-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDadmin(Address): The admin approving
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminProjectNotFound- Project ID does not exist
Example:
approve_renewal(env, project_id, admin_address)?;Purpose: Reject a renewal request (admin-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDadmin(Address): The admin rejecting
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminProjectNotFound- Project ID does not exist
Example:
reject_renewal(env, project_id, admin_address)?;Purpose: Get the current renewal request for a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: Option<VerificationRenewalRecord>
Some(VerificationRenewalRecord)if found,Noneif not found.- Contains renewal request details
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None (returns
Noneif renewal request does not exist)
Example:
let renewal = get_renewal_request(env, project_id);Purpose: Get renewal history for a project with pagination.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDstart_index(u32): Starting indexlimit(u32): Maximum records to return
Return Value: Vec<VerificationRenewalRecord>
- Vector of renewal records
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let renewal_history = get_renewal_history(env, project_id, 0, 10);Purpose: Admin: assign a pending verification to a specific admin for review.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDadmin(Address): The admin performing the assignmentassignee(Address): The admin to assign the verification to
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminProjectNotFound- Project ID does not existVerificationNotFound- No pending verification for this project
Example:
assign_verification(env, project_id, admin_address, assignee_address)?;Purpose: Get the admin assigned to review a verification request.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: Result<Option<Address>, ContractError>
Ok(Some(admin_address))if an admin is assignedOk(None)if no admin is assigned- Failure:
ContractError
Authorization:
- None (read-only, permissionless)
Possible Errors:
ProjectNotFound- Project ID does not exist
Example:
if let Some(assigned) = get_assigned_admin(env, project_id)? {
// Assigned admin address
}Purpose: Configure fees for contract operations (admin-only).
Parameters:
env(Env): The contract environmentadmin(Address): The admin setting feestoken(Option): Token address (None for Stellar native, Some for specific token)verification_fee(u128): Fee amount for verification requestsregistration_fee(u128): Fee amount for project registration (if enabled)treasury(Address): Address receiving collected fees
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an admin
Example:
set_fee(env, admin_address, None, 1000000, 500000, treasury_address)?;Purpose: Pay required fee for a project operation.
Parameters:
env(Env): The contract environmentpayer(Address): The address paying the feeproject_id(u64): The project ID the fee is fortoken(Option): Token to pay in (None for native, Some for token contract)
Return Value: Result<(), ContractError>
Authorization:
- Payer must authorize the payment
Possible Errors:
ProjectNotFound- Project ID does not existFeeConfigNotSet- Fee configuration not set upTreasuryNotSet- Treasury address not configuredInsufficientFee- Payment is less than required fee
Example:
pay_fee(env, payer_address, project_id, None)?;Purpose: Check if the fee has been paid for a specific project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: bool
trueif the fee has been paidfalseotherwise
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let paid = is_fee_paid(env, project_id);Purpose: Get the current fee configuration.
Parameters:
env(Env): The contract environment
Return Value: Result<FeeConfig, ContractError>
- Contains:
token(Option): Token used for feesverification_fee(u128): Verification fee amountregistration_fee(u128): Registration fee amount
Authorization:
- None (read-only, permissionless)
Possible Errors:
FeeConfigNotSet- No fee configuration has been set
Example:
let fees = get_fee_config(env)?;Purpose: Pay the required registration fee for a new project.
Parameters:
env(Env): The contract environmentpayer(Address): The address paying the registration feetoken(Option): Token to pay in (None for native, Some for token contract)
Return Value: Result<(), ContractError>
Authorization:
- Payer must authorize the payment
Possible Errors:
FeeConfigNotSet- Fee configuration not set upInsufficientFee- Payment is less than required fee
Example:
pay_registration_fee(env, payer_address, None)?;Purpose: Get fee payment details for a project (payer, amount, token, timestamp).
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: Option<FeePaymentRecord>
Some(record)if a payment existsNoneif no payment found
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
if let Some(payment) = get_fee_payment_details(env, project_id) {
// Use payment details
}Purpose: Get registration fee payment details for an address.
Parameters:
env(Env): The contract environmentaddress(Address): The payer address
Return Value: Option<FeePaymentRecord>
Some(record)if a payment existsNoneif no payment found
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
if let Some(payment) = get_reg_fee_payment_details(env, payer_address) {
// Use payment details
}Purpose: Report a project for spam, scams, broken links, or abuse.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID to reportreporter(Address): The address reportingreason_cid(String): IPFS CID containing detailed reason
Return Value: Result<(), ContractError>
Authorization:
- Any address can report a project
Possible Errors:
ProjectNotFound- Project ID does not existAlreadyReported- Caller has already reported this projectInvalidReportReason- Reason is invalid
Example:
report_project(env, project_id, reporter_address, String::from_slice(&env, "QmXxxx..."))?;Purpose: Get all reports for a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: Vec<ProjectReport>
- A vector of all reports, containing:
project_id(u64): The projectreporter(Address): Who reportedreason_cid(String): CID of reasontimestamp(u64): Report timestamp
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let reports = get_project_reports(env, project_id);Purpose: Get the number of reports for a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: u32
- Count of reports
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let report_count = get_project_report_count(env, project_id);Purpose: Check if a user has already reported a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDreporter(Address): The reporter address
Return Value: bool
trueif user has reported,falseotherwise
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let has_reported = has_user_reported(env, project_id, user_address);Purpose: Clear all reports for a project (admin-only).
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDadmin(Address): The admin clearing reports
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminProjectNotFound- Project ID does not exist
Example:
clear_project_reports(env, project_id, admin_address)?;Purpose: Create a new curated collection of projects (admin-only).
Parameters:
env(Env): The contract environmentadmin(Address): The admin creating the collectionname(String): Collection namedescription(String): Collection description
Return Value: Result<u64, ContractError>
- Success:
Ok(collection_id)- The ID of the created collection - Failure:
ContractError
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminCollectionExists- Collection with same name already exists
Example:
let collection_id = create_collection(env, admin_address,
String::from_slice(&env, "DeFi Projects"),
String::from_slice(&env, "Top decentralized finance projects"))?;Purpose: Update collection name and description (admin-only).
Parameters:
env(Env): The contract environmentadmin(Address): The admin updatingcollection_id(u64): The collection IDname(String): New collection namedescription(String): New description
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminCollectionNotFound- Collection ID does not existCollectionExists- New name conflicts with existing collection
Example:
update_collection(env, admin_address, collection_id,
String::from_slice(&env, "Updated Name"),
String::from_slice(&env, "Updated description"))?;Purpose: Delete a collection and its project associations (admin-only).
Parameters:
env(Env): The contract environmentadmin(Address): The admin deletingcollection_id(u64): The collection ID
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminCollectionNotFound- Collection ID does not exist
Example:
delete_collection(env, admin_address, collection_id)?;Purpose: Add a project to a collection (admin-only).
Parameters:
env(Env): The contract environmentadmin(Address): The admin addingcollection_id(u64): The collection IDproject_id(u64): The project ID to add
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminCollectionNotFound- Collection ID does not existProjectNotFound- Project ID does not existAlreadyInCollection- Project already in collection
Example:
add_project_to_collection(env, admin_address, collection_id, project_id)?;Purpose: Remove a project from a collection (admin-only).
Parameters:
env(Env): The contract environmentadmin(Address): The admin removingcollection_id(u64): The collection IDproject_id(u64): The project ID to remove
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminCollectionNotFound- Collection ID does not existProjectNotFound- Project ID does not exist
Example:
remove_project_from_collection(env, admin_address, collection_id, project_id)?;Purpose: Retrieve a collection by ID.
Parameters:
env(Env): The contract environmentcollection_id(u64): The collection ID
Return Value: Option<Collection>
Some(Collection)if found,Noneif not found.- Contains:
id(u64): Collection IDname(String): Collection namedescription(String): Descriptioncreated_at(u64): Creation timestampupdated_at(u64): Last update timestamp
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None (returns
Noneif collection ID does not exist)
Example:
let collection = get_collection(env, collection_id);Purpose: List all collections with pagination.
Parameters:
env(Env): The contract environmentstart(u32): Starting indexlimit(u32): Maximum collections to return
Return Value: Vec<Collection>
- Vector of collections
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let collections = list_collections(env, 0, 20);Purpose: List project IDs in a collection with pagination.
Parameters:
env(Env): The contract environmentcollection_id(u64): The collection IDstart(u32): Starting indexlimit(u32): Maximum project IDs to return
Return Value: Vec<u64>
- Vector of project IDs
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let project_ids = list_collection_projects(env, collection_id, 0, 50);Purpose: Get the number of projects in a collection.
Parameters:
env(Env): The contract environmentcollection_id(u64): The collection ID
Return Value: u32
- Count of projects in collection
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let count = get_collection_project_count(env, collection_id);Purpose: Get the total number of collections.
Parameters:
env(Env): The contract environment
Return Value: u64
- Total collection count
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let total = get_collection_count(env);Purpose: Retrieve a single admin action log entry by ID.
Parameters:
env(Env): The contract environmentlog_id(u64): The log entry ID
Return Value: Option<AdminActionEntry>
Some(entry)if found,Noneotherwise- Contains:
id(u64): Log entry IDadmin(Address): Admin who performed actionaction_type(AdminActionType): Type of actiontarget_id(Option): Affected project/collection IDtarget_address(Option): Affected addresstimestamp(u64): Action timestampreason_cid(Option): CID of reason/details
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
if let Some(entry) = get_admin_action_log_entry(env, log_id) {
// Use log entry
}Purpose: List admin action log entries with pagination (most recent first).
Parameters:
env(Env): The contract environmentstart(u32): Starting indexlimit(u32): Maximum entries to return
Return Value: Vec<AdminActionEntry>
- Vector of admin action entries
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let recent_actions = list_admin_actions(env, 0, 100);Purpose: Get the total number of admin action log entries.
Parameters:
env(Env): The contract environment
Return Value: u64
- Total number of log entries
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let total_actions = get_admin_action_log_count(env);Purpose: Open a dispute claiming a project is a duplicate of another.
Parameters:
env(Env): The contract environmentproject_id(u64): The project suspected of being duplicateoriginal_project_id(u64): The project claimed to be the originalcreator(Address): The address opening the disputeevidence_cid(String): IPFS CID containing evidence of duplication
Return Value: Result<u64, ContractError>
- Success:
Ok(dispute_id)- The ID of the created dispute - Failure:
ContractError
Authorization:
- Any address can open a dispute
Possible Errors:
ProjectNotFound- One or both project IDs do not exist
Example:
let dispute_id = open_duplicate_dispute(env, project_id, original_project_id, creator_address, String::from_slice(&env, "QmXxxx..."))?;Purpose: Resolve a duplicate dispute with an action (admin-only).
Parameters:
env(Env): The contract environmentdispute_id(u64): The dispute IDadmin(Address): The admin resolvingaction(DisputeResolutionAction): The resolution action:Reject- Reject the dispute claimArchiveProject(project_id)- Archive the suspected duplicateLinkDuplicates- Link the two projects as related
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminProjectNotFound- Associated project not found
Example:
resolve_duplicate_dispute(env, dispute_id, admin_address, DisputeResolutionAction::ArchiveProject(project_id))?;Purpose: Retrieve a duplicate dispute by ID.
Parameters:
env(Env): The contract environmentdispute_id(u64): The dispute ID
Return Value: Option<DuplicateDispute>
Some(dispute)if found,Noneotherwise- Contains:
id(u64): Dispute IDproject_id(u64): Suspected duplicate projectoriginal_project_id(u64): Claimed original projectcreator(Address): Who opened the disputeevidence_cid(String): Evidence CIDstatus(DisputeStatus): Pending/Rejected/Resolvedcreated_at(u64): Creation timestampresolved_at(u64): Resolution timestamp
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
if let Some(dispute) = get_duplicate_dispute(env, dispute_id) {
// Use dispute data
}Purpose: Get all duplicate disputes for a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: Vec<DuplicateDispute>
- Vector of all disputes (both as reported project and as original)
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let disputes = get_disputes_for_project(env, project_id);Purpose: Extend Time-to-Live for a project and its related data.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: None (void)
Authorization:
- None (permissionless)
Possible Errors:
- None
Example:
extend_project_ttl(env, project_id);Purpose: Extend TTL for a specific review.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDreviewer(Address): The reviewer address
Return Value: None (void)
Authorization:
- None (permissionless)
Possible Errors:
- None
Example:
extend_review_ttl(env, project_id, reviewer_address);Purpose: Extend TTL for all admin-related data for an admin.
Parameters:
env(Env): The contract environmentadmin(Address): The admin address
Return Value: None (void)
Authorization:
- None (permissionless)
Possible Errors:
- None
Example:
extend_admin_ttl(env, admin_address);Purpose: Extend TTL for critical contract configuration (admin list, fee config, treasury).
Parameters:
env(Env): The contract environment
Return Value: None (void)
Authorization:
- None (permissionless)
Possible Errors:
- None
Example:
extend_critical_config_ttl(env);Purpose: Extend TTL for user-related data (owner projects, user reviews).
Parameters:
env(Env): The contract environmentuser(Address): The user address
Return Value: None (void)
Authorization:
- None (permissionless)
Possible Errors:
- None
Example:
extend_user_ttl(env, user_address);Purpose: Extend TTL for verification data.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: None (void)
Authorization:
- None (permissionless)
Possible Errors:
- None
Example:
extend_verification_ttl(env, project_id);Purpose: Follow (subscribe to) a project for updates.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID to followfollower(Address): The address following the project
Return Value: Result<(), ContractError>
Authorization:
- Follower must authorize (self-authenticated)
Possible Errors:
ProjectNotFound- Project ID does not exist
Example:
follow_project(env, project_id, follower_address)?;Purpose: Unfollow (unsubscribe from) a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID to unfollowfollower(Address): The address unfollowing
Return Value: Result<(), ContractError>
Authorization:
- Follower must authorize (self-authenticated)
Possible Errors:
ProjectNotFound- Project ID does not exist
Example:
unfollow_project(env, project_id, follower_address)?;Purpose: Get the number of followers for a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: u32
- Number of followers
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let count = get_follower_count(env, project_id);Purpose: Check if a user is following a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDuser(Address): The user address
Return Value: bool
trueif following,falseotherwise
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let following = is_following(env, project_id, user_address);Purpose: Get the list of followers for a project with pagination.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDstart(u32): Starting index for paginationlimit(u32): Maximum followers to return
Return Value: Vec<Address>
- A vector of follower addresses
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let followers = get_project_followers(env, project_id, 0, 20);Purpose: Get all projects a user is following with pagination.
Parameters:
env(Env): The contract environmentuser(Address): The user addressstart(u32): Starting index for paginationlimit(u32): Maximum subscriptions to return
Return Value: Vec<u64>
- A vector of project IDs the user follows
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let subscriptions = get_user_subscriptions(env, user_address, 0, 50);Purpose: Bookmark a project for later reference.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID to bookmarkuser(Address): The user bookmarking the project
Return Value: Result<(), BookmarkError>
Authorization:
- User must authorize (self-authenticated)
Possible Errors:
ProjectNotFound- Project ID does not existAlreadyBookmarked- Project already bookmarked by this user
Example:
bookmark_project(env, project_id, user_address)?;Purpose: Remove a project bookmark.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID to unbookmarkuser(Address): The user unbookmarking
Return Value: Result<(), BookmarkError>
Authorization:
- User must authorize (self-authenticated)
Possible Errors:
BookmarkNotFound- Project is not bookmarked by this user
Example:
unbookmark_project(env, project_id, user_address)?;Purpose: Check if a user has bookmarked a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDuser(Address): The user address
Return Value: bool
trueif bookmarked,falseotherwise
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let bookmarked = is_bookmarked(env, project_id, user_address);Purpose: Get all bookmarked project IDs for a user with pagination.
Parameters:
env(Env): The contract environmentuser(Address): The user addressstart(u32): Starting index for paginationlimit(u32): Maximum bookmarks to return
Return Value: Vec<u64>
- A vector of bookmarked project IDs
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let bookmarks = get_user_bookmarks(env, user_address, 0, 50);Purpose: Endorse a project as a trusted or high-quality project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID to endorseuser(Address): The user endorsing
Return Value: Result<(), EndorsementError>
Authorization:
- User must authorize (self-authenticated)
Possible Errors:
ProjectNotFound- Project ID does not existAlreadyEndorsed- User has already endorsed this project
Example:
endorse_project(env, project_id, user_address)?;Purpose: Remove an endorsement from a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDuser(Address): The user unendorsing
Return Value: Result<(), EndorsementError>
Authorization:
- User must authorize (self-authenticated)
Possible Errors:
EndorsementNotFound- User has not endorsed this project
Example:
unendorse_project(env, project_id, user_address)?;Purpose: Get the number of endorsements for a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project ID
Return Value: u32
- Number of endorsements
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let count = get_endorsement_count(env, project_id);Purpose: Check if a user has endorsed a project.
Parameters:
env(Env): The contract environmentproject_id(u64): The project IDuser(Address): The user address
Return Value: bool
trueif endorsed,falseotherwise
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let endorsed = has_endorsed(env, project_id, user_address);Purpose: Schedule a fee configuration change to be executed at a future timestamp (admin-only).
Parameters:
env(Env): The contract environmentadmin(Address): The admin schedulingtoken(Option): Token addressverification_fee(u128): New verification feeregistration_fee(u128): New registration feetreasury(Address): New treasury addressexecution_timestamp(u64): Unix timestamp for execution
Return Value: Result<u64, ContractError>
- Success:
Ok(action_id)- The scheduled action ID
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an admin
Example:
let action_id = schedule_set_fee(env, admin_address, None, 1000000, 500000, treasury, future_timestamp)?;Purpose: Schedule adding a new admin at a future timestamp (admin-only).
Parameters:
env(Env): The contract environmentadmin(Address): The admin schedulingnew_admin(Address): The address to promoteexecution_timestamp(u64): Unix timestamp for execution
Return Value: Result<u64, ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an admin
Example:
let action_id = schedule_add_admin(env, admin_address, new_admin_address, future_timestamp)?;Purpose: Schedule removing an admin at a future timestamp (admin-only).
Parameters:
env(Env): The contract environmentadmin(Address): The admin schedulingadmin_to_remove(Address): The admin to removeexecution_timestamp(u64): Unix timestamp for execution
Return Value: Result<u64, ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an admin
Example:
let action_id = schedule_remove_admin(env, admin_address, admin_to_remove, future_timestamp)?;Purpose: Cancel a pending scheduled action (admin-only).
Parameters:
env(Env): The contract environmentcaller(Address): The admin cancellingaction_id(u64): The scheduled action ID
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an adminActionNotFound- Action ID does not exist
Example:
cancel_scheduled_action(env, admin_address, action_id)?;Purpose: Execute a scheduled fee configuration change after its target timestamp.
Parameters:
env(Env): The contract environmentcaller(Address): Any address can trigger executionaction_id(u64): The scheduled action ID
Return Value: Result<(), ContractError>
Authorization:
- None (anyone can trigger after the scheduled time)
Possible Errors:
ActionNotFound- Action ID does not existActionNotReady- Execution timestamp has not been reached
Example:
execute_scheduled_set_fee(env, caller_address, action_id)?;Purpose: Execute a scheduled admin addition after its target timestamp.
Parameters:
env(Env): The contract environmentcaller(Address): Any address can trigger executionaction_id(u64): The scheduled action ID
Return Value: Result<(), ContractError>
Authorization:
- None (anyone can trigger after the scheduled time)
Possible Errors:
ActionNotFound- Action ID does not existActionNotReady- Execution timestamp has not been reached
Example:
execute_scheduled_add_admin(env, caller_address, action_id)?;Purpose: Execute a scheduled admin removal after its target timestamp.
Parameters:
env(Env): The contract environmentcaller(Address): Any address can trigger executionaction_id(u64): The scheduled action ID
Return Value: Result<(), ContractError>
Authorization:
- None (anyone can trigger after the scheduled time)
Possible Errors:
ActionNotFound- Action ID does not existActionNotReady- Execution timestamp has not been reached
Example:
execute_scheduled_remove_admin(env, caller_address, action_id)?;Purpose: Retrieve a scheduled action by ID.
Parameters:
env(Env): The contract environmentaction_id(u64): The scheduled action ID
Return Value: Option<TimelockAction>
Some(action)if foundNoneif not found
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
if let Some(action) = get_scheduled_action(env, action_id) {
// Use action data
}Purpose: List all scheduled actions with pagination.
Parameters:
env(Env): The contract environmentstart(u32): Starting index for paginationlimit(u32): Maximum actions to return
Return Value: Vec<TimelockAction>
- A vector of scheduled actions
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let actions = list_scheduled_actions(env, 0, 20);Purpose: Get the total number of scheduled actions.
Parameters:
env(Env): The contract environment
Return Value: u64
- Total count of scheduled actions
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let count = get_scheduled_action_count(env);Purpose: Set minimum project age before verification is allowed (admin-only).
Parameters:
env(Env): The contract environmentadmin(Address): The admin setting the valuemin_age_seconds(u64): Minimum age in seconds
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an admin
Example:
set_min_project_age(env, admin_address, 7 * 24 * 60 * 60)?; // 7 daysPurpose: Get the minimum project age setting.
Parameters:
env(Env): The contract environment
Return Value: u64
- Minimum age in seconds
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let min_age = get_min_project_age(env);Purpose: Set how long a verification is valid (admin-only).
Parameters:
env(Env): The contract environmentadmin(Address): The admin setting the valueduration_seconds(u64): Duration in seconds (0 = infinite)
Return Value: Result<(), ContractError>
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an admin
Example:
set_verification_duration(env, admin_address, 365 * 24 * 60 * 60)?; // 1 yearPurpose: Get the verification validity duration setting.
Parameters:
env(Env): The contract environment
Return Value: u64
- Duration in seconds
Authorization:
- None (read-only, permissionless)
Possible Errors:
- None
Example:
let duration = get_verification_duration(env);The contract uses these error codes consistently (from ContractError enum):
| Error | Code | When It Occurs |
|---|---|---|
AlreadyInitialized |
1 | Contract already initialized |
NotInitialized |
2 | Contract not yet initialized |
OnlyAdmin |
3 | Caller is not an admin |
ProjectNotFound |
4 | Project ID doesn't exist |
NotProjectOwner |
5 | Caller is not the project owner |
SlugAlreadyExists |
6 | Project slug already registered |
InvalidSlug |
7 | Invalid project slug format |
MaxProjectsExceeded |
8 | Contract project limit reached |
MaxReviewsPerUser |
9 | User exceeded maximum reviews |
MaxReviewsPerProject |
10 | Project exceeded maximum reviews |
ReviewNotFound |
11 | Review doesn't exist |
AlreadyReviewed |
12 | Reviewer already reviewed project |
InvalidCategory |
13 | Category validation failed |
InvalidUrl |
14 | URL format validation failed |
InvalidCid |
15 | CID format validation failed |
InvalidWebsite |
18 | Website URL validation failed |
InvalidLogo |
19 | Logo data invalid |
InvalidMetadata |
20 | Metadata invalid |
InvalidTags |
21 | Tag format invalid |
InvalidSocialLinks |
22 | Social link format invalid |
InvalidLauchTimestamp |
23 | Launch timestamp invalid |
AlreadyMaintainer |
25 | Address is already a maintainer |
NotMaintainer |
26 | Address is not a maintainer |
OnlyMaintainerOrOwner |
27 | Only maintainer or owner can perform this action |
CantRemoveSelf |
29 | Cannot remove yourself |
ProjectAlreadyExists |
32 | Project slug already registered (alias) |
InvalidProjectName |
33 | Project name validation failed |
ProjectNameTooLong |
34 | Project name exceeds max length |
InvalidProjectDesc |
35 | Project description validation failed |
ProjectDescTooLong |
36 | Description exceeds max length |
InvalidProjectData |
37 | Project data validation failed |
InvalidProjectSlug |
38 | Project slug validation failed |
InvalidProjectSlugLen |
39 | Project slug length invalid |
InvalidLogoCid |
41 | Logo CID validation failed |
InvalidMetaCid |
42 | Metadata CID validation failed |
Unauthorized |
43 | Caller lacks required authorization |
AdminOnly |
44 | Caller is not an admin |
AdminNotFound |
45 | Admin address not found |
VerificationNotFound |
46 | No verification record found |
VerificationNotPend |
47 | Verification is not in pending state |
InvalidStatus |
48 | Invalid verification status value |
ProjectTooYoung |
49 | Project doesn't meet minimum age |
VerifiedFieldFrozen |
50 | Cannot modify a verified project field |
AlreadyArchived |
51 | Project already archived |
ProjectNotArchived |
52 | Project not archived |
TransferNotFound |
53 | No pending transfer found |
NotTransferRecip |
54 | Caller is not transfer recipient |
ReservedName |
55 | Project name is reserved |
FeeMissing |
56 | Required fee has not been paid |
FeeInvalid |
57 | Fee configuration is invalid |
FeeAlreadyPaid |
58 | Fee has already been paid |
SecurityContactInvalid |
59 | Security contact validation failed |
DuplicateProjectName |
60 | Normalized project name already exists |
// 1. Register a project
let project_id = register_project(env, ProjectRegistrationParams {
owner: owner_address,
name: String::from_slice(&env, "MyDeFiToken"),
slug: String::from_slice(&env, "mydefitoken"),
description: String::from_slice(&env, "A decentralized finance token"),
category: String::from_slice(&env, "DeFi"),
website: Some(String::from_slice(&env, "https://mydefi.com")),
logo_cid: Some(String::from_slice(&env, "QmXxxx...")),
metadata_cid: None,
tags: Some(vec![&env, String::from_slice(&env, "token"), String::from_slice(&env, "defi")]),
social_links: None,
launch_timestamp: None,
})?;
// 2. Update project information
update_project(env, ProjectUpdateParams {
project_id,
caller: owner_address,
name: Some(String::from_slice(&env, "MyDeFi Token v2")),
..defaults..
})?;
// 3. Add dependencies
add_project_dependency(env, project_id, owner_address, ProjectDependency {
reference: DependencyRef {
project_id: Some(other_project_id),
external_cid: None,
external_url: None,
},
label: Some(String::from_slice(&env, "core-dependency")),
metadata_cid: None,
added_at: env.ledger().timestamp(),
updated_at: env.ledger().timestamp(),
})?;
// 4. Request verification
request_verification(env, project_id, owner_address, String::from_slice(&env, "QmEvidence..."))?;
// 5. Admin approves verification
approve_verification(env, project_id, admin_address)?;
// 6. Retrieve and display project
if let Some(project) = get_project(env, project_id) {
// Use project data for frontend display
}// 1. Add review as a user
add_review(env, project_id, reviewer_address, 4, Some(String::from_slice(&env, "QmReview...")))?;
// 2. Get project statistics
let stats = get_project_stats(env, project_id);
// stats.average_rating, stats.review_count
// 3. Project owner responds to review
respond_to_review(env, project_id, owner_address, reviewer_address, String::from_slice(&env, "Thank you!"))?;
// 4. Get all reviews for a project
let reviews = list_reviews(env, project_id, 0, 50);
// 5. Report an inappropriate review
report_review(env, project_id, reviewer_address, reporter_address)?;
// 6. Admin hides the reported review
hide_review(env, project_id, reviewer_address, admin_address)?;// 1. Create a curated collection
let collection_id = create_collection(env, admin_address,
String::from_slice(&env, "Top DeFi Projects"),
String::from_slice(&env, "Curated list of the best DeFi protocols"))?;
// 2. Add projects to collection
add_project_to_collection(env, admin_address, collection_id, project_id1)?;
add_project_to_collection(env, admin_address, collection_id, project_id2)?;
// 3. Get collection details
let collection = get_collection(env, collection_id)?;
// 4. List projects in collection
let project_ids = list_collection_projects(env, collection_id, 0, 100);
let projects = get_projects_by_ids(env, project_ids);
// 5. Update collection info
update_collection(env, admin_address, collection_id,
String::from_slice(&env, "Top 10 DeFi Projects"),
String::from_slice(&env, "Updated curated list"))?;// 1. User reports duplicate
let dispute_id = open_duplicate_dispute(env, suspect_project_id, original_project_id, reporter_address, String::from_slice(&env, "QmDuplicate..."))?;
// 2. Admin reviews and resolves
if let Some(dispute) = get_duplicate_dispute(env, dispute_id) {
// Review evidence, then resolve
resolve_duplicate_dispute(env, dispute_id, admin_address, DisputeResolutionAction::LinkDuplicates)?;
}- Authorization Checks: All state-modifying operations verify caller authorization
- Data Validation: All inputs are validated for format, length, and content
- Unique Constraints: Project slugs and other identifiers are enforced as unique
- Immutable Records: Verification and review records maintain tamper-proof timestamps
- Admin Action Logging: All admin actions are logged for auditability
- Fee Handling: Fee collection requires proper treasury and token configuration
- TTL Management: Data expiry is managed to prevent bloat on persistent storage
- Always check return types: Functions return
ResultorOption- handle both success and failure cases - Validate project ownership: For owner-only operations, verify ownership before calling
- Use pagination: For list operations, use appropriate
start_id(project ID cursor) orstart_index(list offset) withlimitto avoid timeouts - Cache project data: Once retrieved, cache project data locally when possible
- Monitor admin actions: Regularly review admin action logs for compliance
- Handle duplicates gracefully: Use dispute resolution for duplicate detection
- Extend TTLs proactively: Call TTL extension functions during maintenance windows
- Test with realistic data: Test with actual project metadata and verification scenarios
This documentation matches the current implementation as of June 2024. For updates, refer to the contract source code in the repository.
Frontends and indexers need a single, stable read of the contract's current configuration. get_config returns fees, treasury, admin count, approval threshold, pause state, version, and user-facing limits in one round-trip.
Purpose: Return the aggregated, read-only contract configuration snapshot. Replaces the need to fan out calls to get_fee_config, get_admin_count, get_admin_approval_threshold, etc.
Parameters:
env(Env): The contract environment
Return Value: Result<ContractConfigView, ContractError>
- A fully-populated
ContractConfigViewstruct:version(String): Semantic version of the contract (CONTRACT_VERSION).admin_count(u32): Number of registered admins.admin_approval_threshold(u32): Approval threshold for multi-admin proposals.paused(bool): Global pause flag toggled viaset_pause.treasury(Option<Address>): Treasury address that receives fees.Noneuntilset_feeis invoked.fees(FeeConfig): Token + verification + registration fee amounts. Defaults toNone/0/0untilset_feeis invoked.limits(ContractLimits): User-facing limits surfaced for client validation (max page limit, max projects per user, max reviews per project, max name/description length, verification validity period).
Authorization:
- None (read-only, permissionless)
Possible Errors: None in normal operation; returns Ok even before set_fee is called (zero-fee defaults). The "never configured" state is distinguishable from "configured-with-zero-fees" via treasury: Option<Address> — only set_fee populates it.
Stability: The shape of ContractConfigView / ContractLimits is part of the public contract surface. Only append new fields at the end; never reorder, rename, or remove existing fields without bumping CONTRACT_VERSION.
Example:
let cfg = get_config(env)?;
println!("contract version {}", cfg.version);
println!("paused = {}", cfg.paused);Purpose: Admin-only toggle of the global pause flag surfaced by get_config. Records an audit-log entry on every transition.
Parameters:
env(Env): The contract environmentadmin(Address): The admin toggling the flag (must be a current admin)paused(bool):trueto pause,falseto resume
Return Value: Result<bool, ContractError>
- Returns the pause state before the call (so callers can detect transitions without an extra read).
- Other admin entry points in this contract return
(); the previous-value return is intentional for this method.
Authorization:
- Caller must be an admin
Possible Errors:
AdminOnly- Caller is not an admin
Audit logging:
- Records
AdminActionType::ContractPausedwhen togglingtrue. - Records
AdminActionType::ContractResumedwhen togglingfalse.
Scope: This method only writes the flag. Enforcement across mutating entry points (register_project, pay_fee, …) is intentionally out of scope — see the future pause-enforcement ticket. Frontends should treat the flag as advisory for now.
Example:
let _previous = set_pause(env, admin_address, true)?;