Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add hint #13

Merged
merged 7 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/interface/naming.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ use naming::naming::main::Naming::{Discount, DomainData};
#[starknet::interface]
trait INaming<TContractState> {
// view
fn resolve(self: @TContractState, domain: Span<felt252>, field: felt252) -> felt252;
fn resolve(
self: @TContractState, domain: Span<felt252>, field: felt252, hint: Span<felt252>
) -> felt252;

fn domain_to_data(self: @TContractState, domain: Span<felt252>) -> DomainData;

fn domain_to_id(self: @TContractState, domain: Span<felt252>) -> u128;

fn domain_to_address(self: @TContractState, domain: Span<felt252>) -> ContractAddress;
fn domain_to_address(
self: @TContractState, domain: Span<felt252>, hint: Span<felt252>
) -> ContractAddress;

fn address_to_domain(self: @TContractState, address: ContractAddress) -> Span<felt252>;

Expand Down Expand Up @@ -43,6 +47,8 @@ trait INaming<TContractState> {

fn reset_address_to_domain(ref self: TContractState);

fn set_domain_to_resolver(ref self: TContractState, domain: Span<felt252>, resolver: ContractAddress);

// admin
fn set_admin(ref self: TContractState, new_admin: ContractAddress);

Expand Down
4 changes: 3 additions & 1 deletion src/interface/resolver.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ use starknet::ContractAddress;

#[starknet::interface]
trait IResolver<TContractState> {
fn resolve(self: @TContractState, domain: Span<felt252>, field: felt252) -> felt252;
fn resolve(
self: @TContractState, domain: Span<felt252>, field: felt252, hint: Span<felt252>
) -> felt252;
}
4 changes: 2 additions & 2 deletions src/naming/internal.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,14 @@ impl InternalImpl of InternalTrait {

// returns domain_hash (or zero) and its value for a specific field
fn resolve_util(
self: @Naming::ContractState, domain: Span<felt252>, field: felt252
self: @Naming::ContractState, domain: Span<felt252>, field: felt252, hint: Span<felt252>
) -> (felt252, felt252) {
let (resolver, parent_start) = self.domain_to_resolver(domain, 1);
if (resolver != ContractAddressZeroable::zero()) {
(
0,
IResolverDispatcher { contract_address: resolver }
.resolve(domain.slice(0, parent_start), field)
.resolve(domain.slice(0, parent_start), field, hint)
)
} else {
let hashed_domain = self.hash_domain(domain);
Expand Down
45 changes: 38 additions & 7 deletions src/naming/main.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -148,17 +148,21 @@ mod Naming {
// For example, it allows to find the Bitcoin address of Alice.stark by calling
// naming.resolve(['alice'], 'bitcoin')
// Use it with caution in smartcontracts as it can call untrusted contracts
fn resolve(self: @ContractState, domain: Span<felt252>, field: felt252) -> felt252 {
let (_, value) = self.resolve_util(domain, field);
fn resolve(
self: @ContractState, domain: Span<felt252>, field: felt252, hint: Span<felt252>
) -> felt252 {
let (_, value) = self.resolve_util(domain, field, hint);
value
}

// This functions allows to resolve a domain to a native address. Its output is designed
// to be used as a parameter for other functions (for example if you want to send ERC20
// to a .stark)
fn domain_to_address(self: @ContractState, domain: Span<felt252>) -> ContractAddress {
fn domain_to_address(
self: @ContractState, domain: Span<felt252>, hint: Span<felt252>
) -> ContractAddress {
// resolve must be performed first because it calls untrusted resolving contracts
let (hashed_domain, value) = self.resolve_util(domain, 'starknet');
let (hashed_domain, value) = self.resolve_util(domain, 'starknet', hint);
if value != 0 {
let addr: Option<ContractAddress> = value.try_into();
return addr.unwrap();
Expand Down Expand Up @@ -204,7 +208,8 @@ mod Naming {
fn address_to_domain(self: @ContractState, address: ContractAddress) -> Span<felt252> {
let mut domain = ArrayTrait::new();
self.read_address_to_domain(address, ref domain);
if domain.len() != 0 && self.domain_to_address(domain.span()) == address {
if domain.len() != 0
&& self.domain_to_address(domain.span(), array![].span()) == address {
domain.span()
} else {
let identity = IIdentityDispatcher {
Expand All @@ -215,7 +220,10 @@ mod Naming {
let id_hashed_domain = identity
.get_verifier_data(id, 'name', get_contract_address(), 0);
let domain = self.unhash_domain(id_hashed_domain);
assert(self.domain_to_address(domain) == address, 'domain not pointing back');
assert(
self.domain_to_address(domain, array![].span()) == address,
'domain not pointing back'
);
domain
}
}
Expand Down Expand Up @@ -356,7 +364,10 @@ mod Naming {
// will override your main id
fn set_address_to_domain(ref self: ContractState, domain: Span<felt252>) {
let address = get_caller_address();
assert(self.domain_to_address(domain) == address, 'domain not pointing back');
assert(
self.domain_to_address(domain, array![].span()) == address,
'domain not pointing back'
);
self.emit(Event::AddressToDomainUpdate(AddressToDomainUpdate { address, domain }));
self.set_address_to_domain_util(address, domain);
}
Expand All @@ -372,6 +383,26 @@ mod Naming {
self.set_address_to_domain_util(address, array![0].span());
}

fn set_domain_to_resolver(
ref self: ContractState, domain: Span<felt252>, resolver: ContractAddress
) {
self.assert_control_domain(domain, get_caller_address());

// Write domain owner
let hashed_domain = self.hash_domain(domain);
let current_domain_data = self._domain_data.read(hashed_domain);
let new_domain_data = DomainData {
owner: current_domain_data.owner,
resolver,
address: current_domain_data.address,
expiry: current_domain_data.expiry,
key: current_domain_data.key,
parent_key: current_domain_data.parent_key,
};
self._domain_data.write(hashed_domain, new_domain_data);
self.emit(Event::DomainResolverUpdate(DomainResolverUpdate { domain, resolver }));
}

// ADMIN

fn set_admin(ref self: ContractState, new_admin: ContractAddress) {
Expand Down
10 changes: 6 additions & 4 deletions src/tests/naming/test_custom_resolver.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ mod CustomResolver {

#[external(v0)]
impl AdditionResolveImpl of IResolver<ContractState> {
fn resolve(self: @ContractState, mut domain: Span<felt252>, field: felt252) -> felt252 {
fn resolve(
self: @ContractState, mut domain: Span<felt252>, field: felt252, hint: Span<felt252>
) -> felt252 {
let mut output = 0;
loop {
match domain.pop_front() {
Expand Down Expand Up @@ -88,12 +90,12 @@ fn test_custom_resolver() {

let domain = array![th0rgal].span();
// by default we should have nothing written
assert(naming.resolve(domain, 'starknet') == 0, 'non empty starknet field');
assert(naming.resolve(domain, 'starknet', array![].span()) == 0, 'non empty starknet field');
// so it should resolve to the starknetid owner
assert(naming.domain_to_address(domain) == caller, 'wrong domain target');
assert(naming.domain_to_address(domain, array![].span()) == caller, 'wrong domain target');

let domain = array![1, 2, 3, th0rgal].span();

// let's try the resolving
assert(naming.resolve(domain, 'starknet') == 1 + 2 + 3, 'wrong target');
assert(naming.resolve(domain, 'starknet', array![].span()) == 1 + 2 + 3, 'wrong target');
}
7 changes: 5 additions & 2 deletions src/tests/naming/test_features.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ fn test_subdomains() {
// we transfer hello.th0rgal.stark to id2
naming.transfer_domain(subdomain, id2);

assert(naming.domain_to_address(subdomain) == caller, 'wrong subdomain initial target');
assert(
naming.domain_to_address(subdomain, array![].span()) == caller,
'wrong subdomain initial target'
);

// and make sure the owner has been updated
assert(naming.domain_to_id(subdomain) == id2, 'owner not updated correctly');
Expand All @@ -78,7 +81,7 @@ fn test_subdomains() {

// ensure the subdomain was reset
assert(
naming.domain_to_address(subdomain) == ContractAddressZeroable::zero(),
naming.domain_to_address(subdomain, array![].span()) == ContractAddressZeroable::zero(),
'target not updated correctly'
);
}
Expand Down
44 changes: 39 additions & 5 deletions src/tests/naming/test_usecases.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ fn test_basic_usage() {
// let's try the resolving
let domain = array![th0rgal].span();
// by default we should have nothing written
assert(naming.resolve(domain, 'starknet') == 0, 'non empty starknet field');
assert(naming.resolve(domain, 'starknet', array![].span()) == 0, 'non empty starknet field');
// so it should resolve to the starknetid owner
assert(naming.domain_to_address(domain) == caller, 'wrong domain target');
assert(naming.domain_to_address(domain, array![].span()) == caller, 'wrong domain target');

// let's try reverse resolving
identity.set_main_id(id);
Expand All @@ -65,9 +65,12 @@ fn test_basic_usage() {
identity.set_user_data(id, 'starknet', new_target.into(), 0);

// now we should have nothing written
assert(naming.resolve(domain, 'starknet') == new_target.into(), 'wrong starknet field');
assert(
naming.resolve(domain, 'starknet', array![].span()) == new_target.into(),
'wrong starknet field'
);
// and it should resolve to the new domain target
assert(naming.domain_to_address(domain) == new_target, 'wrong domain target');
assert(naming.domain_to_address(domain, array![].span()) == new_target, 'wrong domain target');

// testing ownership transfer
let new_id = 2;
Expand Down Expand Up @@ -249,7 +252,9 @@ fn test_set_address_to_domain() {

// let's try the resolving
let first_domain = array![first_domain_top].span();
assert(naming.domain_to_address(first_domain) == caller, 'wrong domain target');
assert(
naming.domain_to_address(first_domain, array![].span()) == caller, 'wrong domain target'
);

// set reverse resolving
identity.set_main_id(id1);
Expand All @@ -267,3 +272,32 @@ fn test_set_address_to_domain() {
let expect_domain1 = naming.address_to_domain(caller);
assert(expect_domain1 == first_domain, 'wrong rev resolving b');
}

#[test]
#[available_gas(2000000000)]
fn test_set_domain_to_resolver() {
// setup
let (eth, pricing, identity, naming) = deploy();
let caller = contract_address_const::<0x123>();
set_contract_address(caller);
let id1: u128 = 1;
let domain: felt252 = 82939898252385817;

//we mint the id
identity.mint(id1);

// buy the domain
let (_, price1) = pricing.compute_buy_price(11, 365);
eth.approve(naming.contract_address, price1);
naming
.buy(
id1, domain, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0, 0
);

// set resolver
let resolver = contract_address_const::<0x456>();
naming.set_domain_to_resolver(array![domain].span(), resolver);

let data = naming.domain_to_data(array![domain].span());
assert(data.resolver == resolver, 'wrong resolver');
}