DubheSuiContractsAddress System

Address System

Every Dubhe DApp stores data indexed by a resource address — a string key that uniquely identifies the owner of a storage slot. The address_system module is responsible for deriving this key from the transaction context.


ensure_origin

use dubhe::address_system;
 
let sender: String = address_system::ensure_origin(ctx);

ensure_origin reads ctx and returns the caller’s address as a lowercase hex string (without the 0x prefix).

For native Sui transactions this is always a 64-character hex string derived from the 32-byte Sui address:

ctx.sender() = 0x1462cab50fe5998f8161378e5265f7920bfd9fbce604d602619962f608837217
ensure_origin → "1462cab50fe5998f8161378e5265f7920bfd9fbce604d602619962f608837217"

The resource_address convention

Rule: Every DApp system function that writes user data must derive resource_address from address_system::ensure_origin(ctx). Do not accept the address as a free caller-supplied argument.

Violating this rule means any caller can supply an arbitrary string and overwrite another user’s storage slot.

The counter template follows this convention correctly:

module counter::counter_system {
    use dubhe::dapp_service::DappHub;
    use dubhe::address_system;
    use counter::value;
 
    public entry fun inc(dapp_hub: &mut DappHub, number: u32, ctx: &mut TxContext) {
        // Derive the caller's namespace key — never accept this as a parameter.
        let sender = address_system::ensure_origin(ctx);
 
        if (value::has(dapp_hub, sender)) {
            value::set(dapp_hub, sender, value::get(dapp_hub, sender) + number, ctx);
        } else {
            value::set(dapp_hub, sender, number, ctx);
        }
    }
}

All code generated by dubhe schemagen follows this convention. When writing hand-coded system functions, apply the same pattern.


Address conversion utilities

The module also provides helpers for converting between chain address formats:

use dubhe::address_system;
use std::ascii::string;
 
// Convert an EVM hex address to its mapped Sui address
let sui_addr: address = address_system::evm_to_sui(
    string(b"0x9168765ee952de7c6f8fc6fad5ec209b960b7622")
);
 
// Convert a Solana Base58 address to its mapped Sui address
let sui_addr: address = address_system::solana_to_sui(
    string(b"3vy8k1NAc3Q9EPvqrAuS4DG4qwbgVqfxznEdtcrL743L")
);

Both functions abort if the input is malformed (wrong length or invalid encoding).


Multi-chain support

ensure_origin is designed to support transactions relayed from EVM and Solana chains via Dubhe Channel (the Dubhe bridge relay service, currently under development). When a cross-chain transaction is received, ensure_origin automatically returns the original EVM or Solana address string instead of the Sui address, keeping user namespace isolation intact across chains.

For native Sui DApps this is transparent — ensure_origin behaves exactly like a typed ctx.sender().


Testing

Use the built-in test helpers to simulate different chain contexts in unit tests:

#[test]
public fun test_inc_records_by_sender() {
    let sender = @0x1462cab50fe5998f8161378e5265f7920bfd9fbce604d602619962f608837217;
    let mut scenario = test_scenario::begin(sender);
    let ctx = test_scenario::ctx(&mut scenario);
 
    let origin = address_system::ensure_origin(ctx);
    assert!(origin == string(b"1462cab50fe5998f8161378e5265f7920bfd9fbce604d602619962f608837217"));
 
    scenario.end();
}
 
// Simulate an EVM relay context (requires Dubhe Channel for production use)
#[test]
public fun test_evm_context() {
    let mut scenario = test_scenario::begin(@0x1);
    address_system::setup_evm_scenario(
        &mut scenario,
        b"0x9168765EE952de7C6f8fC6FaD5Ec209B960b7622"
    );
    let ctx = test_scenario::ctx(&mut scenario);
 
    // Returns the original 40-char EVM address (lowercase, no 0x)
    let origin = address_system::ensure_origin(ctx);
    assert!(origin == string(b"9168765ee952de7c6f8fc6fad5ec209b960b7622"));
 
    scenario.end();
}

The setup_evm_scenario and setup_solana_scenario helpers are #[test_only] and are never available in production builds.