Implement a feature that allows issuers to attach off-chain metadata references (e.g., IPFS or HTTPS URIs) to offerings in the Revora revenue-sharing smart contract.
This is a Soroban smart contract (Stellar blockchain) written in Rust. The contract manages revenue-sharing offerings where issuers can register offerings, report revenue, and manage blacklists. You need to extend this functionality to support metadata storage.
- Storage: Support storing a short string or hash reference per offering (issuer, token pair)
- Authorization: Only the issuer or IssuerAdmin can update metadata for their offerings
- Events: Emit events when metadata is created or updated
- CRUD Operations: Implement methods to set, update, and retrieve metadata
- Must work within Soroban's storage model (persistent storage)
- Storage limits: Keep metadata references reasonably sized (suggest max 256 bytes)
- Must handle serialization properly using
#[contracttype] - Must respect the existing freeze and pause mechanisms
- Must verify offering exists before allowing metadata operations
git checkout -b feature/offering-metadata-storageAdd to src/lib.rs:
New DataKey variant:
#[contracttype]
#[derive(Clone)]
pub enum DataKey {
// ... existing variants ...
/// Per (issuer, token): metadata reference (IPFS hash, HTTPS URI, etc.)
OfferingMetadata(Address, Address),
}New event symbols:
const EVENT_METADATA_SET: Symbol = symbol_short!("meta_set");
const EVENT_METADATA_UPDATED: Symbol = symbol_short!("meta_upd");Add these public methods to the RevoraRevenueShareClient:
Set/Update Metadata:
/// Set or update metadata reference for an offering.
/// Only callable by the current issuer of the offering.
/// Returns error if offering doesn't exist or caller is not authorized.
pub fn set_offering_metadata(
env: Env,
issuer: Address,
token: Address,
metadata: String,
) -> Result<(), RevoraError>Get Metadata:
/// Retrieve metadata reference for an offering.
/// Returns None if no metadata has been set.
pub fn get_offering_metadata(
env: Env,
issuer: Address,
token: Address,
) -> Option<String>Authorization checks:
- Verify the offering exists using
get_offering() - Verify caller is the current issuer using
get_current_issuer() - Call
require_not_frozen()andrequire_not_paused() - Call
issuer.require_auth()
Storage operations:
- Use
env.storage().persistent()for metadata storage - Key:
DataKey::OfferingMetadata(issuer, token) - Value:
Stringtype
Event emission:
- Emit
EVENT_METADATA_SETon first set (when no previous metadata exists) - Emit
EVENT_METADATA_UPDATEDon updates (when metadata already exists) - Include issuer, token, and new metadata value in event data
Validation:
- Check metadata length (suggest max 256 bytes)
- Return appropriate error if offering not found
- Handle empty string metadata (allow it for clearing metadata)
Add comprehensive tests to src/test.rs:
Basic CRUD tests:
test_set_offering_metadata_success- Happy path for setting metadatatest_get_offering_metadata_returns_none_initially- No metadata before settest_update_offering_metadata_success- Update existing metadatatest_get_offering_metadata_after_set- Retrieve after setting
Authorization tests:
test_set_metadata_requires_auth- Fails without authtest_set_metadata_requires_issuer- Only issuer can settest_set_metadata_nonexistent_offering- Fails for non-existent offeringtest_set_metadata_respects_freeze- Blocked when contract frozentest_set_metadata_respects_pause- Blocked when contract paused
Edge cases:
test_set_metadata_empty_string- Allow empty metadatatest_set_metadata_max_length- Test size limitstest_set_metadata_oversized_data- Reject if too largetest_set_metadata_repeated_updates- Multiple updates work correctlytest_metadata_scoped_per_offering- Different offerings have separate metadata
Event tests:
test_metadata_set_emits_event- Verify EVENT_METADATA_SET on first settest_metadata_update_emits_event- Verify EVENT_METADATA_UPDATED on updatetest_metadata_events_include_correct_data- Validate event structure
Multi-offering tests:
test_metadata_multiple_offerings_same_issuer- Separate metadata per tokentest_metadata_after_issuer_transfer- Metadata persists after transfer
- Minimum 95% test coverage
- Use
cargo tarpaulinor similar to measure coverage - All error paths must be tested
- All edge cases must be covered
Add clear documentation:
- Inline comments explaining metadata constraints
- Document expected off-chain usage patterns (IPFS, HTTPS URIs)
- Add examples of valid metadata formats
- Document size limits and rationale
Before committing:
# Run all tests
cargo test
# Check for warnings
cargo clippy
# Format code
cargo fmt
# Verify test coverage
cargo tarpaulin --out Htmlfeat(contracts): add per-offering metadata storage
- Add OfferingMetadata DataKey for storing metadata references
- Implement set_offering_metadata() with issuer authorization
- Implement get_offering_metadata() for retrieval
- Add EVENT_METADATA_SET and EVENT_METADATA_UPDATED events
- Enforce 256-byte size limit on metadata strings
- Add comprehensive test suite with 95%+ coverage
- Support IPFS hashes, HTTPS URIs, and other reference formats
- All tests pass (
cargo test) - Test coverage ≥ 95%
- No clippy warnings
- Code properly formatted
- Events emit correctly
- Authorization checks work
- Freeze/pause mechanisms respected
- Edge cases handled
- Documentation complete
96 hours from start
- Metadata is stored as a simple string reference, not the actual content
- Off-chain systems will use these references to fetch actual metadata
- Consider common formats: IPFS CID (Qm...), HTTPS URLs, content hashes
- Metadata is optional - offerings can exist without metadata
- Metadata survives issuer transfers (tied to offering, not issuer)