Who
-
Contract owner
- Deploys/initializes the contract and controls lifecycle.
- Admin methods in
src/lib.rs: - Pause/resume:
pause_contract(),resume_contract()(aliases:pause(),resume()) - Upgrade controls:
propose_upgrade(),cancel_upgrade(),finalize_upgrade() - Signer management:
add_authorized_signer(),remove_authorized_signer()
-
Token holders
- Own, transfer, and burn RODiT tokens.
- Methods:
rodit_transfer(),rodit_burn(), views likerodit_tokens_for_owner()
-
Authorized signers
- Keys permitted to sign fee data for minting (stored in
authorized_signers: UnorderedSet<Vec<u8>>).
- Keys permitted to sign fee data for minting (stored in
-
Integrators
- Use view functions for metadata, tokens, and stats (read-only).
What
A NEAR smart contract for API-access tokens with strong metadata semantics, upgrade discipline, and auditable events.
-
Core types in
src/lib.rs:-
Tokenwithowner_id: AccountId -
TokenMetadatawith API-policy fields (e.g.,openapijson_url,permissioned_routes,jwt_duration,serviceprovider_id) -
JsonTokenview wrapper (token_id, owner_id, metadata) -
RoditContractMetadatawith embedded JSON-LD contextDID_WBA_JSON_LD -
ContractStatus:Active | Paused | Upgrading -
ContractStats:created_at,last_updated_at,total_supply
-
-
Storage layout in
src/lib.rs:tokens_by_id: LookupMap<String, Token>token_metadata_by_id: UnorderedMap<String, TokenMetadata>-
tokens_per_owner: LookupMap<AccountId, Vector<String>>(ordered per owner) authorized_signers: UnorderedSet<Vec<u8>>storage_deposits: LookupMap<AccountId, u128>upgrade_config: upgrade::ContractUpgrade
-
Public API (selected):
- Mutations:
rodit_mint(...),rodit_transfer(...),rodit_burn(...),recover_token(...) - Views:
rodit_token,rodit_tokens,rodit_tokens_for_owner,rodit_total_supply,rodit_supply_for_owner,rodit_tokens_filtered,rodit_metadata,rodit_metadata_jsonld,did_wba_jsonld - Management: pause/resume, upgrade lifecycle, signer management
- Mutations:
Where
-
Code layout:
-
src/lib.rs: Main contract, state, endpoints, and views -
src/events.rs: NEP-297-style event emitters -
src/storage.rs: Storage and fee handling (handle_account_fees_and_storage()) -
src/token_validation.rs: Metadata validation for minting -
src/upgrade.rs: Time-locked upgrade configuration and helpers
-
-
Contract constants:
RODIT_METADATA_SPEC = "RODIT-near.org-20251001"- Issuer URL:
"20251001.rodit.org"
Testnet account for examples:
20251001-rodit-org.testnet
When
-
Initialization:
-
init(owner_id, metadata)sets the owner, applies default metadata (ifNone), initializes collections, setsstatus = Active.
-
-
Lifecycle:
- Most mutating ops require
status == Active(enforced byassert_contract_active()). - Owner can
pause_contract()andresume_contract(). - Upgrading:
propose_upgrade()→ wait enforced delay →finalize_upgrade()(only proposer), with status transitionsActive → Upgrading → Active.
- Most mutating ops require
-
Events:
- Emitted for mint, transfer, burn, recover, fees, and status changes via
src/events.rs.
- Emitted for mint, transfer, burn, recover, fees, and status changes via
Why
-
Deterministic token order per owner:
-
tokens_per_ownerusesVector<String>to preserve acquisition order. -
internal_add_token_to_owner(): prevents duplicates and usesVector::push. -
internal_remove_token_from_owner(): uses indexposition()+swap_remove()for O(1) removal.
-
-
Safety-first state changes:
- Uses checks-effects-interactions.
- Explicit
require!()messages for clear on-chain error debugging.
-
Upgrade discipline:
- Owner-only, proposer-bound finalize, enforced delay, and strict status transitions improve safety.
-
Semantic metadata:
- JSON-LD vocabulary (
DID_WBA_JSON_LD) provides standard, machine-readable fields for API policy and auditing.
- JSON-LD vocabulary (
How
-
Minting (
rodit_mint(...)):- Validates metadata via
validate_token_metadata(...)(src/token_validation.rs). - Ensures attached deposit equals the fee specified in
fee_data_json. - Verifies
fee_signature_base64urlusing an authorized signer. - Persists token + metadata, updates owner vector, updates
total_supplyand timestamps. - Computes actual storage delta with
env::storage_usage()and callshandle_account_fees_and_storage(...)(src/storage.rs). - Emits standardized mint event.
- Validates metadata via
-
Transfer / Burn:
-
rodit_transfer(...): owner-only; moves token between owners’ vectors, updates token owner, emits event. -
rodit_burn(...): owner-only; removes token and metadata, decrements supply, emits event.
-
-
Views:
-
rodit_token(token_id): returnsJsonToken. -
rodit_tokens(from_index, limit): paginates all tokens. -
rodit_tokens_for_owner(account_id, from_index, limit): paginates the owner’s token vector (in acquisition order). -
rodit_tokens_filtered(..., service_provider_id): filters byTokenMetadata.serviceprovider_id. -
rodit_metadata_jsonld(): returns metadata as a JSON-LD document (with embedded@context).
-
-
Upgrade protocol (
src/upgrade.rs):-
propose_upgrade(&mut self): owner-only; sets status toUpgrading, records proposer and timestamp, emits status event. -
finalize_upgrade(&mut self): proposer-only; validates delay has elapsed, resets proposal metadata. -
set_upgrade_delay(new_delay): min 60 seconds.
-
-
Deploy (fresh testnet account):
- Build the contract WASM (e.g., with
cargo near build). - Deploy code to
20251001-rodit-org.testnet. - Call
initwith{"owner_id":"20251001-rodit-org.testnet","metadata":null}.
- Build the contract WASM (e.g., with
Event Schema (Exact)
All events use this envelope (NEP-297 style), per src/events.rs:
{
"standard": "PENDING nepXXX",
"version": "RODIT-near.org-20251001",
"event": "<event_name>",
"data": [ { /* event-specific object */ } ]
}
- rodit_mint
{
"standard": "PENDING nepXXX",
"version": "RODIT-near.org-20251001",
"event": "rodit_mint",
"data": [
{
"owner_id": "<account_id>",
"token_ids": ["<token_id>"]
}
]
}
- rodit_transfer
{
"standard": "PENDING nepXXX",
"version": "RODIT-near.org-20251001",
"event": "rodit_transfer",
"data": [
{
"old_owner_id": "<account_id>",
"new_owner_id": "<account_id>",
"token_ids": ["<token_id>"],
"memo": "<string or null>"
}
]
}
- rodit_burn
{
"standard": "PENDING nepXXX",
"version": "RODIT-near.org-20251001",
"event": "rodit_burn",
"data": [
{
"owner_id": "<account_id>",
"token_ids": ["<token_id>"]
}
]
}
- rodit_recover
{
"standard": "PENDING nepXXX",
"version": "RODIT-near.org-20251001",
"event": "rodit_recover",
"data": [
{
"previous_owner_id": "<account_id>",
"new_owner_id": "<account_id>",
"token_ids": ["<token_id>"]
}
]
}
- rodit_storage_payment
{
"standard": "PENDING nepXXX",
"version": "RODIT-near.org-20251001",
"event": "rodit_storage_payment",
"data": [
{
"account_id": "<account_id>",
"amount": "<yoctoNEAR as string>"
}
]
}
- rodit_contract_fee
{
"standard": "PENDING nepXXX",
"version": "RODIT-near.org-20251001",
"event": "rodit_contract_fee",
"data": [
{
"amount": "<yoctoNEAR as string>"
}
]
}
- rodit_provider_fee
{
"standard": "PENDING nepXXX",
"version": "RODIT-near.org-20251001",
"event": "rodit_provider_fee",
"data": [
{
"provider_id": "<account_id>",
"amount": "<yoctoNEAR as string>"
}
]
}
- rodit_signer_added
{
"standard": "PENDING nepXXX",
"version": "RODIT-near.org-20251001",
"event": "rodit_signer_added",
"data": [
{
"public_key": "<hex-or-base64 string as logged>"
}
]
}
- rodit_signer_removed
{
"standard": "PENDING nepXXX",
"version": "RODIT-near.org-20251001",
"event": "rodit_signer_removed",
"data": [
{
"public_key": "<hex-or-base64 string as logged>"
}
]
}
- rodit_status_change
{
"standard": "PENDING nepXXX",
"version": "RODIT-near.org-20251001",
"event": "rodit_status_change",
"data": [
{
"status": "Active | Paused | Upgrading"
}
]
}
Example Testnet Commands (using near CLI)
- Status and stats
near view 20251001-rodit-org.testnet get_contract_status --network-id testnet
near view 20251001-rodit-org.testnet get_contract_stats --network-id testnet
- Metadata JSON-LD
near view 20251001-rodit-org.testnet rodit_metadata_jsonld --network-id testnet
- Tokens
near view 20251001-rodit-org.testnet rodit_tokens '{"from_index":"0","limit":10}' --network-id testnet
near view 20251001-rodit-org.testnet rodit_tokens_for_owner '{"account_id":"20251001-rodit-org.testnet","from_index":"0","limit":10}' --network-id testnet
- Transfer (example)
near call 20251001-rodit-org.testnet rodit_transfer \
'{"receiver_id":"<RECEIVER>.testnet","token_id":"token-001","memo":null}' \
--account-id 20251001-rodit-org.testnet --network-id testnet
Licensing & Compliance
- All rights reserved.
- For trials/evaluation, contact: rodit@rodit.org
Roadmap
- Next article: how to create two intertwined root RODiT certificates for servers and clients (and their relation to issuance and validation).
- Separate article: fee-signature generation details and best practices.
Summary
The RODiT contract provides a policy-rich token for API access on NEAR testnet with:
- Ordered per-owner token tracking for predictable pagination
- Explicit lifecycle (Active/Paused/Upgrading) with upgrade safety
- JSON-LD-based metadata for semantic interoperability
- Standardized NEP-297-style event logs for observability
Top comments (0)