From 6b2206db044c41e3c258fc8396e97e5e5f721ccd Mon Sep 17 00:00:00 2001 From: Iris Date: Mon, 4 Mar 2024 16:37:46 +0100 Subject: [PATCH 01/10] feat: add altcoin buy & renew support --- Scarb.toml | 1 + scripts/generate_sig.py | 17 ++ src/interface/naming.cairo | 31 +++ src/naming.cairo | 2 +- src/naming/main.cairo | 134 +++++++++++++ src/naming/utils.cairo | 7 + src/pricing.cairo | 14 +- src/tests/naming.cairo | 3 +- src/tests/naming/common.cairo | 6 + src/tests/naming/test_altcoin.cairo | 290 ++++++++++++++++++++++++++++ 10 files changed, 495 insertions(+), 10 deletions(-) create mode 100644 scripts/generate_sig.py create mode 100644 src/tests/naming/test_altcoin.cairo diff --git a/Scarb.toml b/Scarb.toml index c006f31..025a355 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -9,6 +9,7 @@ starknet = "2.3.1" openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", rev = "f3e2a5f0547a429c716f32471b06df729cbdfb9f" } storage_read = { git = "https://github.com/starknet-id/storage_read_component.git", rev = "c6c69e15d34abfc39ac51dc21b96724e2e19ff31" } identity = { git = "https://github.com/starknet-id/identity.git", rev = "eb37846330b78e1410cf5e3e17a5dcca5652f921" } +wadray = { git = "https://github.com/lindy-labs/wadray.git", rev = "ea1fa019e7c7c60878ac1e613bc81693524436f5" } [[target.starknet-contract]] # Enable Sierra codegen. diff --git a/scripts/generate_sig.py b/scripts/generate_sig.py new file mode 100644 index 0000000..9cd691d --- /dev/null +++ b/scripts/generate_sig.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +from starkware.crypto.signature.signature import private_to_stark_key, get_random_private_key, sign +from starknet_py.hash.utils import pedersen_hash + +priv_key = 123 +pub_key = private_to_stark_key(priv_key) +print("pub_key:", hex(pub_key)) + +user_addr = 0x123 +erc20_addr = 0x5 +quote = 1221805004292776 +max_validity = 1000 +encoded_string = 724720344857006587549020016926517802128122613457935427138661 +data = pedersen_hash(pedersen_hash(pedersen_hash(pedersen_hash(user_addr, erc20_addr), quote), max_validity), encoded_string) + +(x, y) = sign(data, priv_key) +print("sig:", hex(x), hex(y)) \ No newline at end of file diff --git a/src/interface/naming.cairo b/src/interface/naming.cairo index 0b15db3..d0f9bb0 100644 --- a/src/interface/naming.cairo +++ b/src/interface/naming.cairo @@ -1,5 +1,6 @@ use starknet::{ContractAddress, ClassHash}; use naming::naming::main::Naming::{Discount, DomainData}; +use wadray::Wad; #[starknet::interface] trait INaming { @@ -32,6 +33,21 @@ trait INaming { metadata: felt252, ); + fn altcoin_buy( + ref self: TContractState, + id: u128, + domain: felt252, + days: u16, + resolver: ContractAddress, + sponsor: ContractAddress, + discount_id: felt252, + metadata: felt252, + altcoin_addr: ContractAddress, + quote: Wad, + max_validity: u64, + sig: (felt252, felt252), + ); + fn renew( ref self: TContractState, domain: felt252, @@ -41,6 +57,19 @@ trait INaming { metadata: felt252, ); + fn altcoin_renew( + ref self: TContractState, + domain: felt252, + days: u16, + sponsor: ContractAddress, + discount_id: felt252, + metadata: felt252, + altcoin_addr: ContractAddress, + quote: Wad, + max_validity: u64, + sig: (felt252, felt252), + ); + fn transfer_domain(ref self: TContractState, domain: Span, target_id: u128); fn reset_subdomains(ref self: TContractState, domain: Span); @@ -71,4 +100,6 @@ trait INaming { fn set_referral_contract(ref self: TContractState, referral_contract: ContractAddress); fn upgrade(ref self: TContractState, new_class_hash: ClassHash); + + fn set_server_pub_key(ref self: TContractState, new_key: felt252); } diff --git a/src/naming.cairo b/src/naming.cairo index a967bc9..d5475a4 100644 --- a/src/naming.cairo +++ b/src/naming.cairo @@ -1,4 +1,4 @@ mod main; mod internal; mod asserts; -mod utils; \ No newline at end of file +mod utils; diff --git a/src/naming/main.cairo b/src/naming/main.cairo index af1f95b..8030bb0 100644 --- a/src/naming/main.cairo +++ b/src/naming/main.cairo @@ -10,6 +10,9 @@ mod Naming { use starknet::class_hash::ClassHash; use integer::{u256_safe_divmod, u256_as_non_zero}; use core::pedersen; + use hash::LegacyHash; + use ecdsa::check_ecdsa_signature; + use wadray::Wad; use naming::{ naming::{asserts::AssertionsTrait, internal::InternalTrait, utils::UtilsTrait}, interface::{ @@ -131,6 +134,7 @@ mod Naming { _domain_data: LegacyMap, _hash_to_domain: LegacyMap<(felt252, usize), felt252>, _address_to_domain: LegacyMap<(ContractAddress, usize), felt252>, + _server_pub_key: felt252, #[substorage(v0)] storage_read: storage_read_component::Storage, } @@ -272,6 +276,64 @@ mod Naming { self.mint_domain(expiry, resolver, hashed_domain, id, domain); } + fn altcoin_buy( + ref self: ContractState, + id: u128, + domain: felt252, + days: u16, + resolver: ContractAddress, + sponsor: ContractAddress, + discount_id: felt252, + metadata: felt252, + altcoin_addr: ContractAddress, + quote: Wad, + max_validity: u64, + sig: (felt252, felt252), + ) { + let (hashed_domain, now, expiry) = self.assert_purchase_is_possible(id, domain, days); + // we need a u256 to be able to perform safe divisions + let domain_len = self.get_chars_len(domain.into()); + + // check quote timestamp is still valid + assert(get_block_timestamp() <= max_validity, 'quotation expired'); + + // verify signature + let altcoin: felt252 = altcoin_addr.into(); + let quote_felt : felt252 = quote.into(); + let message_hash = LegacyHash::hash( + LegacyHash::hash( + LegacyHash::hash( + LegacyHash::hash(get_caller_address().into(), altcoin), quote_felt + ), + max_validity + ), + 'starknet id altcoin quote' + ); + let (sig0, sig1) = sig; + let is_valid = check_ecdsa_signature(message_hash, self._server_pub_key.read(), sig0, sig1); + assert(is_valid, 'Invalid signature'); + + // find domain cost in ETH + let (_, price_in_eth) = IPricingDispatcher { + contract_address: self._pricing_contract.read() + } + .compute_buy_price(domain_len, days); + // compute domain cost in altcoin + let price_in_altcoin = self.get_altcoin_price(quote, price_in_eth.try_into().unwrap()); + self + .pay_domain( + domain_len, + altcoin_addr, + price_in_altcoin, + now, + days, + domain, + sponsor, + discount_id + ); + self.emit(Event::SaleMetadata(SaleMetadata { domain, metadata })); + self.mint_domain(expiry, resolver, hashed_domain, id, domain); + } fn renew( ref self: ContractState, @@ -316,6 +378,73 @@ mod Naming { self.emit(Event::DomainRenewal(DomainRenewal { domain, new_expiry })); } + fn altcoin_renew( + ref self: ContractState, + domain: felt252, + days: u16, + sponsor: ContractAddress, + discount_id: felt252, + metadata: felt252, + altcoin_addr: ContractAddress, + quote: Wad, + max_validity: u64, + sig: (felt252, felt252), + ) { + let now = get_block_timestamp(); + let hashed_domain = self.hash_domain(array![domain].span()); + let domain_data = self._domain_data.read(hashed_domain); + + // check quote timestamp is still valid + assert(get_block_timestamp() <= max_validity, 'quotation expired'); + // verify signature + let altcoin: felt252 = altcoin_addr.into(); + let quote_felt : felt252 = quote.into(); + let message_hash = LegacyHash::hash( + LegacyHash::hash( + LegacyHash::hash( + LegacyHash::hash(get_caller_address().into(), altcoin), quote_felt + ), + max_validity + ), + 'starknet id altcoin quote' + ); + let (sig0, sig1) = sig; + let is_valid = check_ecdsa_signature(message_hash, self._server_pub_key.read(), sig0, sig1); + assert(is_valid, 'Invalid signature'); + + // we need a u256 to be able to perform safe divisions + let domain_len = self.get_chars_len(domain.into()); + // find domain cost in ETH + let (_, price_in_eth) = IPricingDispatcher { + contract_address: self._pricing_contract.read() + } + .compute_renew_price(domain_len, days); + // compute domain cost in altcoin + let price_in_altcoin = self.get_altcoin_price(quote, price_in_eth.try_into().unwrap()); + self.pay_domain(domain_len, altcoin_addr, price_in_altcoin, now, days, domain, sponsor, discount_id); + self.emit(Event::SaleMetadata(SaleMetadata { domain, metadata })); + // find new domain expiry + let new_expiry = if domain_data.expiry <= now { + now + 86400 * days.into() + } else { + domain_data.expiry + 86400 * days.into() + }; + // 25*365 = 9125 + assert(new_expiry <= now + 86400 * 9125, 'purchase too long'); + assert(days >= 6 * 30, 'purchase too short'); + + let data = DomainData { + owner: domain_data.owner, + resolver: domain_data.resolver, + address: domain_data.address, + expiry: new_expiry, + key: domain_data.key, + parent_key: 0, + }; + self._domain_data.write(hashed_domain, data); + self.emit(Event::DomainRenewal(DomainRenewal { domain, new_expiry })); + } + fn transfer_domain(ref self: ContractState, domain: Span, target_id: u128) { self.assert_control_domain(domain, get_caller_address()); @@ -507,6 +636,11 @@ mod Naming { assert(!new_class_hash.is_zero(), 'Class hash cannot be zero'); starknet::replace_class_syscall(new_class_hash).unwrap(); } + + fn set_server_pub_key(ref self: ContractState, new_key: felt252) { + assert(get_caller_address() == self._admin_address.read(), 'you are not admin'); + self._server_pub_key.write(new_key); + } } } diff --git a/src/naming/utils.cairo b/src/naming/utils.cairo index ff767e8..71355a2 100644 --- a/src/naming/utils.cairo +++ b/src/naming/utils.cairo @@ -3,6 +3,7 @@ use naming::{ naming::main::{Naming, Naming::{_hash_to_domain, _hash_to_domainContractMemberStateTrait}} }; use integer::{u256_safe_divmod, u256_as_non_zero}; +use wadray::{Wad, WAD_SCALE}; #[generate_trait] impl UtilsImpl of UtilsTrait { @@ -57,4 +58,10 @@ impl UtilsImpl of UtilsTrait { let next = self.get_chars_len(p); 1 + next } + + fn get_altcoin_price( + self: @Naming::ContractState, altcoin_quote: Wad, domain_price_eth: Wad + ) -> u256 { + (domain_price_eth / altcoin_quote).into() + } } diff --git a/src/pricing.cairo b/src/pricing.cairo index 195c831..fb3566a 100644 --- a/src/pricing.cairo +++ b/src/pricing.cairo @@ -11,11 +11,11 @@ mod Pricing { #[storage] struct Storage { - erc20: ContractAddress, + erc20: ContractAddress, } #[constructor] - fn constructor(ref self: ContractState, erc20_address: ContractAddress, ) { + fn constructor(ref self: ContractState, erc20_address: ContractAddress,) { self.erc20.write(erc20_address); } @@ -25,9 +25,8 @@ mod Pricing { self: @ContractState, domain_len: usize, days: u16 ) -> (ContractAddress, u256) { ( - self.erc20.read(), u256 { - low: self.get_price_per_day(domain_len) * days.into(), high: 0 - } + self.erc20.read(), + u256 { low: self.get_price_per_day(domain_len) * days.into(), high: 0 } ) } @@ -35,9 +34,8 @@ mod Pricing { self: @ContractState, domain_len: usize, days: u16 ) -> (ContractAddress, u256) { ( - self.erc20.read(), u256 { - low: self.get_price_per_day(domain_len) * days.into(), high: 0 - } + self.erc20.read(), + u256 { low: self.get_price_per_day(domain_len) * days.into(), high: 0 } ) } } diff --git a/src/tests/naming.cairo b/src/tests/naming.cairo index d825d5b..0e0700c 100644 --- a/src/tests/naming.cairo +++ b/src/tests/naming.cairo @@ -2,4 +2,5 @@ mod common; mod test_abuses; mod test_custom_resolver; mod test_usecases; -mod test_features; \ No newline at end of file +mod test_features; +mod test_altcoin; diff --git a/src/tests/naming/common.cairo b/src/tests/naming/common.cairo index 4816d62..402ec6f 100644 --- a/src/tests/naming/common.cairo +++ b/src/tests/naming/common.cairo @@ -79,3 +79,9 @@ fn deploy() -> (IERC20CamelDispatcher, IPricingDispatcher, IIdentityDispatcher, INamingDispatcher { contract_address: address } ) } + +fn deploy_stark() -> IERC20CamelDispatcher { + //erc20 + let stark = utils::deploy(ERC20::TEST_CLASS_HASH, array![]); + IERC20CamelDispatcher { contract_address: stark } +} diff --git a/src/tests/naming/test_altcoin.cairo b/src/tests/naming/test_altcoin.cairo new file mode 100644 index 0000000..16d9274 --- /dev/null +++ b/src/tests/naming/test_altcoin.cairo @@ -0,0 +1,290 @@ +use array::ArrayTrait; +use array::SpanTrait; +use option::OptionTrait; +use zeroable::Zeroable; +use traits::{Into, TryInto}; +use starknet::testing; +use starknet::ContractAddress; +use starknet::contract_address::ContractAddressZeroable; +use starknet::contract_address_const; +use starknet::testing::{set_contract_address, set_block_timestamp}; +use identity::{ + identity::main::Identity, interface::identity::{IIdentityDispatcher, IIdentityDispatcherTrait} +}; +use openzeppelin::token::erc20::{ + interface::{IERC20Camel, IERC20CamelDispatcher, IERC20CamelDispatcherTrait} +}; +use naming::interface::naming::{INamingDispatcher, INamingDispatcherTrait}; +use naming::interface::pricing::{IPricingDispatcher, IPricingDispatcherTrait}; +use naming::naming::main::Naming; +use naming::pricing::Pricing; +use naming::naming::utils::UtilsImpl; +use super::common::{deploy, deploy_stark}; +use super::super::utils; +use core::debug::PrintTrait; +use wadray::Wad; + +#[test] +#[available_gas(200000000000)] +fn test_convert_quote_to_eth() { + let mut unsafe_state = Naming::unsafe_new_contract_state(); + + // User wants to buy a domain in STRK for one year + let domain_price_eth = Wad { val: 8999999999999875 }; + // 1 STRK = 0,005221805004292776 ETH + let quote = Wad { val: 5221805004292776 }; + + assert( + UtilsImpl::get_altcoin_price( + @unsafe_state, quote, domain_price_eth + ) == 1723541953903122668_u256, + 'Wrong altcoin price' + ); +} + +#[test] +#[available_gas(2000000000)] +fn test_buy_domain_with_strk() { + // setup + let (eth, pricing, identity, naming) = deploy(); + let strk = deploy_stark(); + let caller = contract_address_const::<0x123>(); + set_contract_address(caller); + let id1: u128 = 1; + let th0rgal: felt252 = 33133781693; + + naming + .set_server_pub_key( + 1162637274776062843434229637044893256148643831598397603392524411337131005673 + ); + set_block_timestamp(500); + + //we mint the ids id + identity.mint(id1); + + // we check how much a domain costs + let quote = Wad { val: 5221805004292776 }; + let (_, price_in_eth) = pricing.compute_buy_price(7, 365); + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; + + // we allow the naming to take our money + strk.approve(naming.contract_address, price_in_strk.into()); + + // we buy with no resolver, no sponsor, no discount and empty metadata + let max_validity = 1000; + let sig = ( + 0x2460d27e5d5f25e2b6450a57853d634f812484e9d7c541adcbd04d9a22f3632, + 0x7f8723da0253c58ebccc036b5060f4538ed4301f40d66f4aa0ba3932adb9b31 + ); + naming + .altcoin_buy( + id1, + th0rgal, + 365, + ContractAddressZeroable::zero(), + ContractAddressZeroable::zero(), + 0, + 0, + strk.contract_address, + quote, + max_validity, + sig + ); + + assert(strk.allowance(caller, naming.contract_address) == 0, 'allowance not reset'); + assert( + naming.domain_to_address(array![th0rgal].span(), array![].span()) == caller, + 'wrong domain target' + ); +} + +#[test] +#[available_gas(2000000000)] +#[should_panic(expected: ('quotation expired', 'ENTRYPOINT_FAILED'))] +fn test_buy_domain_altcoin_quote_expired() { + // setup + let (eth, pricing, identity, naming) = deploy(); + let strk = deploy_stark(); + let caller = contract_address_const::<0x123>(); + set_contract_address(caller); + let id1: u128 = 1; + let th0rgal: felt252 = 33133781693; + + naming + .set_server_pub_key( + 1162637274776062843434229637044893256148643831598397603392524411337131005673 + ); + set_block_timestamp(500); + + //we mint the ids id + identity.mint(id1); + + // we check how much a domain costs + let quote = Wad { val: 5221805004292776 }; + let (_, price_in_eth) = pricing.compute_buy_price(7, 365); + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; + + // we allow the naming to take our money + strk.approve(naming.contract_address, price_in_strk.into()); + + // we buy with no resolver, no sponsor, no discount and empty metadata + let max_validity = 1000; + let sig = ( + 0x2460d27e5d5f25e2b6450a57853d634f812484e9d7c541adcbd04d9a22f3632, + 0x7f8723da0253c58ebccc036b5060f4538ed4301f40d66f4aa0ba3932adb9b31 + ); + + // we try buying after the max_validity timestamp + set_block_timestamp(1500); + naming + .altcoin_buy( + id1, + th0rgal, + 365, + ContractAddressZeroable::zero(), + ContractAddressZeroable::zero(), + 0, + 0, + strk.contract_address, + quote, + max_validity, + sig + ); +} + +#[test] +#[available_gas(2000000000)] +#[should_panic(expected: ('Invalid signature', 'ENTRYPOINT_FAILED'))] +fn test_buy_domain_altcoin_wrong_quote() { + // setup + let (eth, pricing, identity, naming) = deploy(); + let strk = deploy_stark(); + let caller = contract_address_const::<0x123>(); + set_contract_address(caller); + let id1: u128 = 1; + let th0rgal: felt252 = 33133781693; + + naming + .set_server_pub_key( + 1162637274776062843434229637044893256148643831598397603392524411337131005673 + ); + set_block_timestamp(500); + + //we mint the ids id + identity.mint(id1); + + // we check how much a domain costs + let quote = Wad { val: 5221805004292776 }; + let (_, price_in_eth) = pricing.compute_buy_price(7, 365); + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; + + // we allow the naming to take our money + strk.approve(naming.contract_address, price_in_strk.into()); + + // we buy with no resolver, no sponsor, no discount and empty metadata + let max_validity = 1000; + let sig = ( + 0x2460d27e5d5f25e2b6450a57853d634f812484e9d7c541adcbd04d9a22f3632, + 0x7f8723da0253c58ebccc036b5060f4538ed4301f40d66f4aa0ba3932adb9b31 + ); + // we try buying with a quote lower than the actual price + naming + .altcoin_buy( + id1, + th0rgal, + 365, + ContractAddressZeroable::zero(), + ContractAddressZeroable::zero(), + 0, + 0, + strk.contract_address, + Wad { val: 1 }, + max_validity, + sig + ); +} + +#[test] +#[available_gas(2000000000)] +fn test_renew_domain_with_strk() { + // setup + let (eth, pricing, identity, naming) = deploy(); + let strk = deploy_stark(); + let caller = contract_address_const::<0x123>(); + set_contract_address(caller); + let id1: u128 = 1; + let th0rgal: felt252 = 33133781693; + + naming + .set_server_pub_key( + 1162637274776062843434229637044893256148643831598397603392524411337131005673 + ); + + //we mint the ids id + identity.mint(id1); + + // we check how much a domain costs + let quote = Wad { val: 5221805004292776 }; + let (_, price_in_eth) = pricing.compute_buy_price(7, 365); + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; + + // we allow the naming to take our money + strk.approve(naming.contract_address, price_in_strk.into()); + + // we buy with no resolver, no sponsor, no discount and empty metadata + let max_validity = 1000; + let sig = ( + 0x2460d27e5d5f25e2b6450a57853d634f812484e9d7c541adcbd04d9a22f3632, + 0x7f8723da0253c58ebccc036b5060f4538ed4301f40d66f4aa0ba3932adb9b31 + ); + naming + .altcoin_buy( + id1, + th0rgal, + 365, + ContractAddressZeroable::zero(), + ContractAddressZeroable::zero(), + 0, + 0, + strk.contract_address, + quote, + max_validity, + sig + ); + + assert(strk.allowance(caller, naming.contract_address) == 0, 'allowance not reset'); + assert( + naming.domain_to_address(array![th0rgal].span(), array![].span()) == caller, + 'wrong domain target' + ); + + // we check how much a domain costs to renew + let quote = Wad { val: 1221805004292776 }; + let (_, price_in_eth) = pricing.compute_buy_price(7, 365); + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; + + // we allow the naming to take our money + strk.approve(naming.contract_address, price_in_strk.into()); + + // we renew with no sponsor, no discount and empty metadata + let max_validity = 1000; + let sig = ( + 0x35ca6ee2dadda50edb4fe0f50aa2aae356a4d695e1e34dfbecb366a44cb5495, + 0x65d27e9121fc9712781b5a815461049a380ad87aac051f174c5c482195dcb90 + ); + naming.altcoin_renew( + th0rgal, + 365, + ContractAddressZeroable::zero(), + 0, + 0, + strk.contract_address, + quote, + max_validity, + sig + ); + assert(strk.allowance(caller, naming.contract_address) == 0, 'allowance not reset'); + assert(naming.domain_to_data(array![th0rgal].span()).expiry == 2 * 365 * 86400, 'invalid renew expiry'); +} + + From ff4ef2977831cb0f3b30fa401dc2c76ec63a856e Mon Sep 17 00:00:00 2001 From: Iris Date: Mon, 4 Mar 2024 17:55:49 +0100 Subject: [PATCH 02/10] fix: do not include user addr in sig verification --- scripts/generate_sig.py | 2 +- src/naming/main.cairo | 38 ++++++++++++---------- src/tests/naming/test_altcoin.cairo | 49 +++++++++++++++-------------- 3 files changed, 48 insertions(+), 41 deletions(-) diff --git a/scripts/generate_sig.py b/scripts/generate_sig.py index 9cd691d..6c84fea 100644 --- a/scripts/generate_sig.py +++ b/scripts/generate_sig.py @@ -11,7 +11,7 @@ quote = 1221805004292776 max_validity = 1000 encoded_string = 724720344857006587549020016926517802128122613457935427138661 -data = pedersen_hash(pedersen_hash(pedersen_hash(pedersen_hash(user_addr, erc20_addr), quote), max_validity), encoded_string) +data = pedersen_hash(pedersen_hash(pedersen_hash(erc20_addr, quote), max_validity), encoded_string) (x, y) = sign(data, priv_key) print("sig:", hex(x), hex(y)) \ No newline at end of file diff --git a/src/naming/main.cairo b/src/naming/main.cairo index 8030bb0..92a3c04 100644 --- a/src/naming/main.cairo +++ b/src/naming/main.cairo @@ -299,18 +299,15 @@ mod Naming { // verify signature let altcoin: felt252 = altcoin_addr.into(); - let quote_felt : felt252 = quote.into(); + let quote_felt: felt252 = quote.into(); let message_hash = LegacyHash::hash( - LegacyHash::hash( - LegacyHash::hash( - LegacyHash::hash(get_caller_address().into(), altcoin), quote_felt - ), - max_validity - ), + LegacyHash::hash(LegacyHash::hash(altcoin, quote_felt), max_validity), 'starknet id altcoin quote' ); let (sig0, sig1) = sig; - let is_valid = check_ecdsa_signature(message_hash, self._server_pub_key.read(), sig0, sig1); + let is_valid = check_ecdsa_signature( + message_hash, self._server_pub_key.read(), sig0, sig1 + ); assert(is_valid, 'Invalid signature'); // find domain cost in ETH @@ -398,18 +395,15 @@ mod Naming { assert(get_block_timestamp() <= max_validity, 'quotation expired'); // verify signature let altcoin: felt252 = altcoin_addr.into(); - let quote_felt : felt252 = quote.into(); + let quote_felt: felt252 = quote.into(); let message_hash = LegacyHash::hash( - LegacyHash::hash( - LegacyHash::hash( - LegacyHash::hash(get_caller_address().into(), altcoin), quote_felt - ), - max_validity - ), + LegacyHash::hash(LegacyHash::hash(altcoin, quote_felt), max_validity), 'starknet id altcoin quote' ); let (sig0, sig1) = sig; - let is_valid = check_ecdsa_signature(message_hash, self._server_pub_key.read(), sig0, sig1); + let is_valid = check_ecdsa_signature( + message_hash, self._server_pub_key.read(), sig0, sig1 + ); assert(is_valid, 'Invalid signature'); // we need a u256 to be able to perform safe divisions @@ -421,7 +415,17 @@ mod Naming { .compute_renew_price(domain_len, days); // compute domain cost in altcoin let price_in_altcoin = self.get_altcoin_price(quote, price_in_eth.try_into().unwrap()); - self.pay_domain(domain_len, altcoin_addr, price_in_altcoin, now, days, domain, sponsor, discount_id); + self + .pay_domain( + domain_len, + altcoin_addr, + price_in_altcoin, + now, + days, + domain, + sponsor, + discount_id + ); self.emit(Event::SaleMetadata(SaleMetadata { domain, metadata })); // find new domain expiry let new_expiry = if domain_data.expiry <= now { diff --git a/src/tests/naming/test_altcoin.cairo b/src/tests/naming/test_altcoin.cairo index 16d9274..7a35d60 100644 --- a/src/tests/naming/test_altcoin.cairo +++ b/src/tests/naming/test_altcoin.cairo @@ -73,8 +73,8 @@ fn test_buy_domain_with_strk() { // we buy with no resolver, no sponsor, no discount and empty metadata let max_validity = 1000; let sig = ( - 0x2460d27e5d5f25e2b6450a57853d634f812484e9d7c541adcbd04d9a22f3632, - 0x7f8723da0253c58ebccc036b5060f4538ed4301f40d66f4aa0ba3932adb9b31 + 0x2d46882b7601332cab0b45a44c5da71d7cb8698d2aaa3eee1c777430047b4b1, + 0x2eaebd6d46827e5bb1fd5c1a96c85f5dfbf3b77df03627545594e695867348a ); naming .altcoin_buy( @@ -130,8 +130,8 @@ fn test_buy_domain_altcoin_quote_expired() { // we buy with no resolver, no sponsor, no discount and empty metadata let max_validity = 1000; let sig = ( - 0x2460d27e5d5f25e2b6450a57853d634f812484e9d7c541adcbd04d9a22f3632, - 0x7f8723da0253c58ebccc036b5060f4538ed4301f40d66f4aa0ba3932adb9b31 + 0x2d46882b7601332cab0b45a44c5da71d7cb8698d2aaa3eee1c777430047b4b1, + 0x2eaebd6d46827e5bb1fd5c1a96c85f5dfbf3b77df03627545594e695867348a ); // we try buying after the max_validity timestamp @@ -184,8 +184,8 @@ fn test_buy_domain_altcoin_wrong_quote() { // we buy with no resolver, no sponsor, no discount and empty metadata let max_validity = 1000; let sig = ( - 0x2460d27e5d5f25e2b6450a57853d634f812484e9d7c541adcbd04d9a22f3632, - 0x7f8723da0253c58ebccc036b5060f4538ed4301f40d66f4aa0ba3932adb9b31 + 0x2d46882b7601332cab0b45a44c5da71d7cb8698d2aaa3eee1c777430047b4b1, + 0x2eaebd6d46827e5bb1fd5c1a96c85f5dfbf3b77df03627545594e695867348a ); // we try buying with a quote lower than the actual price naming @@ -234,8 +234,8 @@ fn test_renew_domain_with_strk() { // we buy with no resolver, no sponsor, no discount and empty metadata let max_validity = 1000; let sig = ( - 0x2460d27e5d5f25e2b6450a57853d634f812484e9d7c541adcbd04d9a22f3632, - 0x7f8723da0253c58ebccc036b5060f4538ed4301f40d66f4aa0ba3932adb9b31 + 0x2d46882b7601332cab0b45a44c5da71d7cb8698d2aaa3eee1c777430047b4b1, + 0x2eaebd6d46827e5bb1fd5c1a96c85f5dfbf3b77df03627545594e695867348a ); naming .altcoin_buy( @@ -269,22 +269,25 @@ fn test_renew_domain_with_strk() { // we renew with no sponsor, no discount and empty metadata let max_validity = 1000; let sig = ( - 0x35ca6ee2dadda50edb4fe0f50aa2aae356a4d695e1e34dfbecb366a44cb5495, - 0x65d27e9121fc9712781b5a815461049a380ad87aac051f174c5c482195dcb90 - ); - naming.altcoin_renew( - th0rgal, - 365, - ContractAddressZeroable::zero(), - 0, - 0, - strk.contract_address, - quote, - max_validity, - sig + 0x42768490cdba55ef41ac540caab9a9ec4133b5d1f42289d2c32f5c1efc07f65, + 0x15d56a36d5fa94dc183ef32f4f9bc3d7f0d4b68b8b07a4541cad11a8c9cf7f6 ); + naming + .altcoin_renew( + th0rgal, + 365, + ContractAddressZeroable::zero(), + 0, + 0, + strk.contract_address, + quote, + max_validity, + sig + ); assert(strk.allowance(caller, naming.contract_address) == 0, 'allowance not reset'); - assert(naming.domain_to_data(array![th0rgal].span()).expiry == 2 * 365 * 86400, 'invalid renew expiry'); + assert( + naming.domain_to_data(array![th0rgal].span()).expiry == 2 * 365 * 86400, + 'invalid renew expiry' + ); } - From 3f7a65dfaae930480b78a310cd9ba16468410437 Mon Sep 17 00:00:00 2001 From: Iris Date: Tue, 5 Mar 2024 11:37:51 +0100 Subject: [PATCH 03/10] test: fix tests --- src/tests/naming/test_altcoin.cairo | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/tests/naming/test_altcoin.cairo b/src/tests/naming/test_altcoin.cairo index 7a35d60..621d4b1 100644 --- a/src/tests/naming/test_altcoin.cairo +++ b/src/tests/naming/test_altcoin.cairo @@ -31,7 +31,7 @@ fn test_convert_quote_to_eth() { // User wants to buy a domain in STRK for one year let domain_price_eth = Wad { val: 8999999999999875 }; - // 1 STRK = 0,005221805004292776 ETH + // 1 STRK = 0,00522180500429277 ETH let quote = Wad { val: 5221805004292776 }; assert( @@ -291,3 +291,27 @@ fn test_renew_domain_with_strk() { ); } +#[test] +#[available_gas(200000000000)] +fn test_hash_matches() { + let contract = contract_address_const::< + 0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d + >(); + let erc20_addr: felt252 = contract.into(); + + let quote: Wad = Wad { val: 607843394028633 }; + let quote_felt: felt252 = quote.into(); + + let max_validity: felt252 = 1709635880; + + let message_hash = core::hash::LegacyHash::hash( + core::hash::LegacyHash::hash( + core::hash::LegacyHash::hash(erc20_addr, quote_felt), max_validity + ), + 'starknet id altcoin quote' + ); + assert( + message_hash == 0x00b693e8796152c46cbef85de6c8880520aad37af639702a70a3d907ff5cb114, + 'wrong hash' + ); +} From 2e27f08157146a3688e0a7b010167a3c7cdea84a Mon Sep 17 00:00:00 2001 From: Iris Date: Tue, 5 Mar 2024 18:02:31 +0100 Subject: [PATCH 04/10] fix: quote typing --- src/interface/naming.cairo | 5 ++--- src/naming/main.cairo | 10 ++++++---- src/tests/naming/test_altcoin.cairo | 22 +++++++++++----------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/interface/naming.cairo b/src/interface/naming.cairo index d0f9bb0..7b18ad5 100644 --- a/src/interface/naming.cairo +++ b/src/interface/naming.cairo @@ -1,6 +1,5 @@ use starknet::{ContractAddress, ClassHash}; use naming::naming::main::Naming::{Discount, DomainData}; -use wadray::Wad; #[starknet::interface] trait INaming { @@ -43,7 +42,7 @@ trait INaming { discount_id: felt252, metadata: felt252, altcoin_addr: ContractAddress, - quote: Wad, + quote: u128, max_validity: u64, sig: (felt252, felt252), ); @@ -65,7 +64,7 @@ trait INaming { discount_id: felt252, metadata: felt252, altcoin_addr: ContractAddress, - quote: Wad, + quote: u128, max_validity: u64, sig: (felt252, felt252), ); diff --git a/src/naming/main.cairo b/src/naming/main.cairo index 92a3c04..e8ab66a 100644 --- a/src/naming/main.cairo +++ b/src/naming/main.cairo @@ -286,7 +286,7 @@ mod Naming { discount_id: felt252, metadata: felt252, altcoin_addr: ContractAddress, - quote: Wad, + quote: u128, max_validity: u64, sig: (felt252, felt252), ) { @@ -316,7 +316,8 @@ mod Naming { } .compute_buy_price(domain_len, days); // compute domain cost in altcoin - let price_in_altcoin = self.get_altcoin_price(quote, price_in_eth.try_into().unwrap()); + let price_in_altcoin = self + .get_altcoin_price(quote.into(), price_in_eth.try_into().unwrap()); self .pay_domain( domain_len, @@ -383,7 +384,7 @@ mod Naming { discount_id: felt252, metadata: felt252, altcoin_addr: ContractAddress, - quote: Wad, + quote: u128, max_validity: u64, sig: (felt252, felt252), ) { @@ -414,7 +415,8 @@ mod Naming { } .compute_renew_price(domain_len, days); // compute domain cost in altcoin - let price_in_altcoin = self.get_altcoin_price(quote, price_in_eth.try_into().unwrap()); + let price_in_altcoin = self + .get_altcoin_price(quote.into(), price_in_eth.try_into().unwrap()); self .pay_domain( domain_len, diff --git a/src/tests/naming/test_altcoin.cairo b/src/tests/naming/test_altcoin.cairo index 621d4b1..b25b1ba 100644 --- a/src/tests/naming/test_altcoin.cairo +++ b/src/tests/naming/test_altcoin.cairo @@ -63,9 +63,9 @@ fn test_buy_domain_with_strk() { identity.mint(id1); // we check how much a domain costs - let quote = Wad { val: 5221805004292776 }; + let quote = 5221805004292776_u128; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote.into(); // we allow the naming to take our money strk.approve(naming.contract_address, price_in_strk.into()); @@ -120,9 +120,9 @@ fn test_buy_domain_altcoin_quote_expired() { identity.mint(id1); // we check how much a domain costs - let quote = Wad { val: 5221805004292776 }; + let quote = 5221805004292776_u128; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote.into(); // we allow the naming to take our money strk.approve(naming.contract_address, price_in_strk.into()); @@ -174,9 +174,9 @@ fn test_buy_domain_altcoin_wrong_quote() { identity.mint(id1); // we check how much a domain costs - let quote = Wad { val: 5221805004292776 }; + let quote = 5221805004292776_u128; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote.into(); // we allow the naming to take our money strk.approve(naming.contract_address, price_in_strk.into()); @@ -198,7 +198,7 @@ fn test_buy_domain_altcoin_wrong_quote() { 0, 0, strk.contract_address, - Wad { val: 1 }, + 1, max_validity, sig ); @@ -224,9 +224,9 @@ fn test_renew_domain_with_strk() { identity.mint(id1); // we check how much a domain costs - let quote = Wad { val: 5221805004292776 }; + let quote = 5221805004292776_u128; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote.into(); // we allow the naming to take our money strk.approve(naming.contract_address, price_in_strk.into()); @@ -259,9 +259,9 @@ fn test_renew_domain_with_strk() { ); // we check how much a domain costs to renew - let quote = Wad { val: 1221805004292776 }; + let quote = 1221805004292776_u128; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote.into(); // we allow the naming to take our money strk.approve(naming.contract_address, price_in_strk.into()); From 6cd4a0467a9502e6870733bf167d4a439cd447e7 Mon Sep 17 00:00:00 2001 From: Iris Date: Wed, 6 Mar 2024 12:02:17 +0100 Subject: [PATCH 05/10] feat: add altcoin_renew_subscription function --- src/interface/naming.cairo | 15 +++++ src/naming/main.cairo | 64 +++++++++++++++++++++ src/tests/naming/test_altcoin.cairo | 86 +++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) diff --git a/src/interface/naming.cairo b/src/interface/naming.cairo index 7b18ad5..e918c85 100644 --- a/src/interface/naming.cairo +++ b/src/interface/naming.cairo @@ -69,6 +69,17 @@ trait INaming { sig: (felt252, felt252), ); + fn altcoin_renew_subscription( + ref self: TContractState, + domain: felt252, + days: u16, + sponsor: ContractAddress, + discount_id: felt252, + metadata: felt252, + altcoin_addr: ContractAddress, + price_in_altcoin: u256, + ); + fn transfer_domain(ref self: TContractState, domain: Span, target_id: u128); fn reset_subdomains(ref self: TContractState, domain: Span); @@ -101,4 +112,8 @@ trait INaming { fn upgrade(ref self: TContractState, new_class_hash: ClassHash); fn set_server_pub_key(ref self: TContractState, new_key: felt252); + + fn whitelist_renewal_contract(ref self: TContractState, contract: ContractAddress); + + fn blacklist_renewal_contract(ref self: TContractState, contract: ContractAddress); } diff --git a/src/naming/main.cairo b/src/naming/main.cairo index e8ab66a..8959f76 100644 --- a/src/naming/main.cairo +++ b/src/naming/main.cairo @@ -135,6 +135,7 @@ mod Naming { _hash_to_domain: LegacyMap<(felt252, usize), felt252>, _address_to_domain: LegacyMap<(ContractAddress, usize), felt252>, _server_pub_key: felt252, + _whitelisted_renewal_contracts: LegacyMap, #[substorage(v0)] storage_read: storage_read_component::Storage, } @@ -451,6 +452,59 @@ mod Naming { self.emit(Event::DomainRenewal(DomainRenewal { domain, new_expiry })); } + fn altcoin_renew_subscription( + ref self: ContractState, + domain: felt252, + days: u16, + sponsor: ContractAddress, + discount_id: felt252, + metadata: felt252, + altcoin_addr: ContractAddress, + price_in_altcoin: u256, + ) { + let now = get_block_timestamp(); + let hashed_domain = self.hash_domain(array![domain].span()); + let domain_data = self._domain_data.read(hashed_domain); + + // check caller is a whitelisted altcoin auto renewal contract + assert(self._whitelisted_renewal_contracts.read(get_caller_address()), 'Caller not whitelisted'); + + // we need a u256 to be able to perform safe divisions + let domain_len = self.get_chars_len(domain.into()); + self + .pay_domain( + domain_len, + altcoin_addr, + price_in_altcoin, + now, + days, + domain, + sponsor, + discount_id + ); + self.emit(Event::SaleMetadata(SaleMetadata { domain, metadata })); + // find new domain expiry + let new_expiry = if domain_data.expiry <= now { + now + 86400 * days.into() + } else { + domain_data.expiry + 86400 * days.into() + }; + // 25*365 = 9125 + assert(new_expiry <= now + 86400 * 9125, 'purchase too long'); + assert(days >= 6 * 30, 'purchase too short'); + + let data = DomainData { + owner: domain_data.owner, + resolver: domain_data.resolver, + address: domain_data.address, + expiry: new_expiry, + key: domain_data.key, + parent_key: 0, + }; + self._domain_data.write(hashed_domain, data); + self.emit(Event::DomainRenewal(DomainRenewal { domain, new_expiry })); + } + fn transfer_domain(ref self: ContractState, domain: Span, target_id: u128) { self.assert_control_domain(domain, get_caller_address()); @@ -647,6 +701,16 @@ mod Naming { assert(get_caller_address() == self._admin_address.read(), 'you are not admin'); self._server_pub_key.write(new_key); } + + fn whitelist_renewal_contract(ref self: ContractState, contract: ContractAddress) { + assert(get_caller_address() == self._admin_address.read(), 'you are not admin'); + self._whitelisted_renewal_contracts.write(contract, true); + } + + fn blacklist_renewal_contract(ref self: ContractState, contract: ContractAddress) { + assert(get_caller_address() == self._admin_address.read(), 'you are not admin'); + self._whitelisted_renewal_contracts.write(contract, false); + } } } diff --git a/src/tests/naming/test_altcoin.cairo b/src/tests/naming/test_altcoin.cairo index b25b1ba..05a2244 100644 --- a/src/tests/naming/test_altcoin.cairo +++ b/src/tests/naming/test_altcoin.cairo @@ -315,3 +315,89 @@ fn test_hash_matches() { 'wrong hash' ); } + +#[test] +#[available_gas(2000000000)] +fn test_subscription_with_strk() { + // setup + let (eth, pricing, identity, naming) = deploy(); + let strk = deploy_stark(); + let caller = contract_address_const::<0x123>(); + set_contract_address(caller); + let id1: u128 = 1; + let th0rgal: felt252 = 33133781693; + naming + .set_server_pub_key( + 1162637274776062843434229637044893256148643831598397603392524411337131005673 + ); + + // we whitelist renewal contract + let renewal_contract = contract_address_const::<0x456>(); + naming.whitelist_renewal_contract(renewal_contract); + + //we mint the ids id + identity.mint(id1); + + // we check how much a domain costs + let quote = 5221805004292776_u128; + let (_, price_in_eth) = pricing.compute_buy_price(7, 365); + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote.into(); + + // we allow the naming to take our money + strk.approve(naming.contract_address, price_in_strk.into()); + + // we buy with no resolver, no sponsor, no discount and empty metadata + let max_validity = 1000; + let sig = ( + 0x2d46882b7601332cab0b45a44c5da71d7cb8698d2aaa3eee1c777430047b4b1, + 0x2eaebd6d46827e5bb1fd5c1a96c85f5dfbf3b77df03627545594e695867348a + ); + naming + .altcoin_buy( + id1, + th0rgal, + 365, + ContractAddressZeroable::zero(), + ContractAddressZeroable::zero(), + 0, + 0, + strk.contract_address, + quote, + max_validity, + sig + ); + + assert(strk.allowance(caller, naming.contract_address) == 0, 'allowance not reset'); + assert( + naming.domain_to_address(array![th0rgal].span(), array![].span()) == caller, + 'wrong domain target' + ); + + // we check how much a domain costs to renew + let quote = 1221805004292776_u128; + let (_, price_in_eth) = pricing.compute_buy_price(7, 365); + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote.into(); + + // to test, we transfer the price of the domain in STRK to the renewal contract + // we allow the naming to take the price of the domain in STRK + strk.transfer(renewal_contract, price_in_strk.into()); + set_contract_address(renewal_contract); + strk.approve(naming.contract_address, price_in_strk.into()); + + // we renew domain through renewal_contract + naming + .altcoin_renew_subscription( + th0rgal, + 365, + ContractAddressZeroable::zero(), + 0, + 0, + strk.contract_address, + price_in_strk.into(), + ); + assert(strk.allowance(caller, naming.contract_address) == 0, 'allowance not reset'); + assert( + naming.domain_to_data(array![th0rgal].span()).expiry == 2 * 365 * 86400, + 'invalid renew expiry' + ); +} From 10e091fb7104ad62387361e9c030aebc4b278309 Mon Sep 17 00:00:00 2001 From: Iris Date: Wed, 6 Mar 2024 12:13:30 +0100 Subject: [PATCH 06/10] test: add test for altcoin_renew_subscription --- src/naming/main.cairo | 5 ++++- src/tests/naming/test_altcoin.cairo | 34 +++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/naming/main.cairo b/src/naming/main.cairo index 8959f76..a821881 100644 --- a/src/naming/main.cairo +++ b/src/naming/main.cairo @@ -467,7 +467,10 @@ mod Naming { let domain_data = self._domain_data.read(hashed_domain); // check caller is a whitelisted altcoin auto renewal contract - assert(self._whitelisted_renewal_contracts.read(get_caller_address()), 'Caller not whitelisted'); + assert( + self._whitelisted_renewal_contracts.read(get_caller_address()), + 'Caller not whitelisted' + ); // we need a u256 to be able to perform safe divisions let domain_len = self.get_chars_len(domain.into()); diff --git a/src/tests/naming/test_altcoin.cairo b/src/tests/naming/test_altcoin.cairo index 05a2244..3d6fb33 100644 --- a/src/tests/naming/test_altcoin.cairo +++ b/src/tests/naming/test_altcoin.cairo @@ -331,10 +331,6 @@ fn test_subscription_with_strk() { 1162637274776062843434229637044893256148643831598397603392524411337131005673 ); - // we whitelist renewal contract - let renewal_contract = contract_address_const::<0x456>(); - naming.whitelist_renewal_contract(renewal_contract); - //we mint the ids id identity.mint(id1); @@ -378,6 +374,10 @@ fn test_subscription_with_strk() { let (_, price_in_eth) = pricing.compute_buy_price(7, 365); let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote.into(); + // we whitelist renewal contract + let renewal_contract = contract_address_const::<0x456>(); + naming.whitelist_renewal_contract(renewal_contract); + // to test, we transfer the price of the domain in STRK to the renewal contract // we allow the naming to take the price of the domain in STRK strk.transfer(renewal_contract, price_in_strk.into()); @@ -401,3 +401,29 @@ fn test_subscription_with_strk() { 'invalid renew expiry' ); } + +#[test] +#[available_gas(2000000000)] +#[should_panic(expected: ('Caller not whitelisted', 'ENTRYPOINT_FAILED'))] +fn test_subscription_not_whitelisted() { + // setup + let (eth, pricing, identity, naming) = deploy(); + let strk = deploy_stark(); + let caller = contract_address_const::<0x123>(); + set_contract_address(caller); + let id1: u128 = 1; + let th0rgal: felt252 = 33133781693; + naming + .set_server_pub_key( + 1162637274776062843434229637044893256148643831598397603392524411337131005673 + ); + + //we mint the ids id + identity.mint(id1); + + // we try to renew domain but we're not whitelisted + naming + .altcoin_renew_subscription( + th0rgal, 365, ContractAddressZeroable::zero(), 0, 0, strk.contract_address, 1.into() + ); +} From d97e6853c8cf9f83c8e9aa77af43b3980a679471 Mon Sep 17 00:00:00 2001 From: Iris Date: Wed, 6 Mar 2024 14:31:17 +0100 Subject: [PATCH 07/10] fix: rename to auto_renew_altcoin --- src/interface/naming.cairo | 2 +- src/naming/main.cairo | 2 +- src/tests/naming/test_altcoin.cairo | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/interface/naming.cairo b/src/interface/naming.cairo index e918c85..c8cd638 100644 --- a/src/interface/naming.cairo +++ b/src/interface/naming.cairo @@ -69,7 +69,7 @@ trait INaming { sig: (felt252, felt252), ); - fn altcoin_renew_subscription( + fn auto_renew_altcoin( ref self: TContractState, domain: felt252, days: u16, diff --git a/src/naming/main.cairo b/src/naming/main.cairo index a821881..4f2cfec 100644 --- a/src/naming/main.cairo +++ b/src/naming/main.cairo @@ -452,7 +452,7 @@ mod Naming { self.emit(Event::DomainRenewal(DomainRenewal { domain, new_expiry })); } - fn altcoin_renew_subscription( + fn auto_renew_altcoin( ref self: ContractState, domain: felt252, days: u16, diff --git a/src/tests/naming/test_altcoin.cairo b/src/tests/naming/test_altcoin.cairo index 3d6fb33..c88a0ff 100644 --- a/src/tests/naming/test_altcoin.cairo +++ b/src/tests/naming/test_altcoin.cairo @@ -386,7 +386,7 @@ fn test_subscription_with_strk() { // we renew domain through renewal_contract naming - .altcoin_renew_subscription( + .auto_renew_altcoin( th0rgal, 365, ContractAddressZeroable::zero(), @@ -423,7 +423,7 @@ fn test_subscription_not_whitelisted() { // we try to renew domain but we're not whitelisted naming - .altcoin_renew_subscription( + .auto_renew_altcoin( th0rgal, 365, ContractAddressZeroable::zero(), 0, 0, strk.contract_address, 1.into() ); } From 05c7b03a481dbf8862b9852f825b5adc7c861edf Mon Sep 17 00:00:00 2001 From: Iris Date: Thu, 7 Mar 2024 09:47:26 +0100 Subject: [PATCH 08/10] fix: type quote as Wad --- src/interface/naming.cairo | 5 +++-- src/naming/main.cairo | 8 ++++---- src/tests/naming/test_altcoin.cairo | 31 +++++++++++++++-------------- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/interface/naming.cairo b/src/interface/naming.cairo index c8cd638..c38c0d9 100644 --- a/src/interface/naming.cairo +++ b/src/interface/naming.cairo @@ -1,5 +1,6 @@ use starknet::{ContractAddress, ClassHash}; use naming::naming::main::Naming::{Discount, DomainData}; +use wadray::Wad; #[starknet::interface] trait INaming { @@ -42,7 +43,7 @@ trait INaming { discount_id: felt252, metadata: felt252, altcoin_addr: ContractAddress, - quote: u128, + quote: Wad, max_validity: u64, sig: (felt252, felt252), ); @@ -64,7 +65,7 @@ trait INaming { discount_id: felt252, metadata: felt252, altcoin_addr: ContractAddress, - quote: u128, + quote: Wad, max_validity: u64, sig: (felt252, felt252), ); diff --git a/src/naming/main.cairo b/src/naming/main.cairo index 4f2cfec..e4a3daf 100644 --- a/src/naming/main.cairo +++ b/src/naming/main.cairo @@ -287,7 +287,7 @@ mod Naming { discount_id: felt252, metadata: felt252, altcoin_addr: ContractAddress, - quote: u128, + quote: Wad, max_validity: u64, sig: (felt252, felt252), ) { @@ -318,7 +318,7 @@ mod Naming { .compute_buy_price(domain_len, days); // compute domain cost in altcoin let price_in_altcoin = self - .get_altcoin_price(quote.into(), price_in_eth.try_into().unwrap()); + .get_altcoin_price(quote, price_in_eth.try_into().unwrap()); self .pay_domain( domain_len, @@ -385,7 +385,7 @@ mod Naming { discount_id: felt252, metadata: felt252, altcoin_addr: ContractAddress, - quote: u128, + quote: Wad, max_validity: u64, sig: (felt252, felt252), ) { @@ -417,7 +417,7 @@ mod Naming { .compute_renew_price(domain_len, days); // compute domain cost in altcoin let price_in_altcoin = self - .get_altcoin_price(quote.into(), price_in_eth.try_into().unwrap()); + .get_altcoin_price(quote, price_in_eth.try_into().unwrap()); self .pay_domain( domain_len, diff --git a/src/tests/naming/test_altcoin.cairo b/src/tests/naming/test_altcoin.cairo index c88a0ff..dfd6a1b 100644 --- a/src/tests/naming/test_altcoin.cairo +++ b/src/tests/naming/test_altcoin.cairo @@ -63,9 +63,9 @@ fn test_buy_domain_with_strk() { identity.mint(id1); // we check how much a domain costs - let quote = 5221805004292776_u128; + let quote = Wad { val: 5221805004292776}; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote.into(); + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; // we allow the naming to take our money strk.approve(naming.contract_address, price_in_strk.into()); @@ -120,9 +120,9 @@ fn test_buy_domain_altcoin_quote_expired() { identity.mint(id1); // we check how much a domain costs - let quote = 5221805004292776_u128; + let quote = Wad { val: 5221805004292776}; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote.into(); + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; // we allow the naming to take our money strk.approve(naming.contract_address, price_in_strk.into()); @@ -174,9 +174,9 @@ fn test_buy_domain_altcoin_wrong_quote() { identity.mint(id1); // we check how much a domain costs - let quote = 5221805004292776_u128; + let quote = Wad { val: 5221805004292776}; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote.into(); + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; // we allow the naming to take our money strk.approve(naming.contract_address, price_in_strk.into()); @@ -188,6 +188,7 @@ fn test_buy_domain_altcoin_wrong_quote() { 0x2eaebd6d46827e5bb1fd5c1a96c85f5dfbf3b77df03627545594e695867348a ); // we try buying with a quote lower than the actual price + let lower_quote = Wad { val: 1}; naming .altcoin_buy( id1, @@ -198,7 +199,7 @@ fn test_buy_domain_altcoin_wrong_quote() { 0, 0, strk.contract_address, - 1, + lower_quote, max_validity, sig ); @@ -224,9 +225,9 @@ fn test_renew_domain_with_strk() { identity.mint(id1); // we check how much a domain costs - let quote = 5221805004292776_u128; + let quote = Wad { val: 5221805004292776}; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote.into(); + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; // we allow the naming to take our money strk.approve(naming.contract_address, price_in_strk.into()); @@ -259,9 +260,9 @@ fn test_renew_domain_with_strk() { ); // we check how much a domain costs to renew - let quote = 1221805004292776_u128; + let quote = Wad { val: 1221805004292776}; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote.into(); + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; // we allow the naming to take our money strk.approve(naming.contract_address, price_in_strk.into()); @@ -335,9 +336,9 @@ fn test_subscription_with_strk() { identity.mint(id1); // we check how much a domain costs - let quote = 5221805004292776_u128; + let quote = Wad { val: 5221805004292776}; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote.into(); + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; // we allow the naming to take our money strk.approve(naming.contract_address, price_in_strk.into()); @@ -370,9 +371,9 @@ fn test_subscription_with_strk() { ); // we check how much a domain costs to renew - let quote = 1221805004292776_u128; + let quote = Wad { val: 1221805004292776}; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote.into(); + let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; // we whitelist renewal contract let renewal_contract = contract_address_const::<0x456>(); From 962a73e3359f2d94d0aee33113166e3835c89353 Mon Sep 17 00:00:00 2001 From: Iris Date: Thu, 7 Mar 2024 09:48:41 +0100 Subject: [PATCH 09/10] fix: remove unecessary expiry checks --- src/naming/main.cairo | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/naming/main.cairo b/src/naming/main.cairo index e4a3daf..a7cd773 100644 --- a/src/naming/main.cairo +++ b/src/naming/main.cairo @@ -492,9 +492,6 @@ mod Naming { } else { domain_data.expiry + 86400 * days.into() }; - // 25*365 = 9125 - assert(new_expiry <= now + 86400 * 9125, 'purchase too long'); - assert(days >= 6 * 30, 'purchase too short'); let data = DomainData { owner: domain_data.owner, From 66c9e1a02f8a7dd5a9c8e5f1a16345fb637d0038 Mon Sep 17 00:00:00 2001 From: Iris Date: Thu, 7 Mar 2024 10:13:36 +0100 Subject: [PATCH 10/10] fix: quote --- scripts/generate_sig.py | 2 +- src/naming/utils.cairo | 2 +- src/tests/naming/test_altcoin.cairo | 58 ++++++++++++++--------------- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/scripts/generate_sig.py b/scripts/generate_sig.py index 6c84fea..a93e109 100644 --- a/scripts/generate_sig.py +++ b/scripts/generate_sig.py @@ -8,7 +8,7 @@ user_addr = 0x123 erc20_addr = 0x5 -quote = 1221805004292776 +quote = 591205338160899000000 max_validity = 1000 encoded_string = 724720344857006587549020016926517802128122613457935427138661 data = pedersen_hash(pedersen_hash(pedersen_hash(erc20_addr, quote), max_validity), encoded_string) diff --git a/src/naming/utils.cairo b/src/naming/utils.cairo index 71355a2..c1667ac 100644 --- a/src/naming/utils.cairo +++ b/src/naming/utils.cairo @@ -62,6 +62,6 @@ impl UtilsImpl of UtilsTrait { fn get_altcoin_price( self: @Naming::ContractState, altcoin_quote: Wad, domain_price_eth: Wad ) -> u256 { - (domain_price_eth / altcoin_quote).into() + (domain_price_eth * altcoin_quote).into() } } diff --git a/src/tests/naming/test_altcoin.cairo b/src/tests/naming/test_altcoin.cairo index dfd6a1b..94fff58 100644 --- a/src/tests/naming/test_altcoin.cairo +++ b/src/tests/naming/test_altcoin.cairo @@ -31,13 +31,13 @@ fn test_convert_quote_to_eth() { // User wants to buy a domain in STRK for one year let domain_price_eth = Wad { val: 8999999999999875 }; - // 1 STRK = 0,00522180500429277 ETH - let quote = Wad { val: 5221805004292776 }; + // 1 ETH = 1591.2053381608991 STRK + let quote = Wad { val: 1591205338160899000000 }; assert( UtilsImpl::get_altcoin_price( @unsafe_state, quote, domain_price_eth - ) == 1723541953903122668_u256, + ) == 14320848043447892099_u256, 'Wrong altcoin price' ); } @@ -63,9 +63,9 @@ fn test_buy_domain_with_strk() { identity.mint(id1); // we check how much a domain costs - let quote = Wad { val: 5221805004292776}; + let quote = Wad { val: 1591205338160899000000}; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; + let price_in_strk: Wad = Wad { val: price_in_eth.low } * quote; // we allow the naming to take our money strk.approve(naming.contract_address, price_in_strk.into()); @@ -73,8 +73,8 @@ fn test_buy_domain_with_strk() { // we buy with no resolver, no sponsor, no discount and empty metadata let max_validity = 1000; let sig = ( - 0x2d46882b7601332cab0b45a44c5da71d7cb8698d2aaa3eee1c777430047b4b1, - 0x2eaebd6d46827e5bb1fd5c1a96c85f5dfbf3b77df03627545594e695867348a + 0x45bab8945c7ebe23192a98a496e1f13929ca8fc8edaf810212f0ee00aab9d1c, + 0x17f03434193b85c7f24354e7de98e4a4bc1e5bd9d26021be3cb26b9c80b282c ); naming .altcoin_buy( @@ -120,9 +120,9 @@ fn test_buy_domain_altcoin_quote_expired() { identity.mint(id1); // we check how much a domain costs - let quote = Wad { val: 5221805004292776}; + let quote = Wad { val: 1591205338160899000000}; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; + let price_in_strk: Wad = Wad { val: price_in_eth.low } * quote; // we allow the naming to take our money strk.approve(naming.contract_address, price_in_strk.into()); @@ -130,8 +130,8 @@ fn test_buy_domain_altcoin_quote_expired() { // we buy with no resolver, no sponsor, no discount and empty metadata let max_validity = 1000; let sig = ( - 0x2d46882b7601332cab0b45a44c5da71d7cb8698d2aaa3eee1c777430047b4b1, - 0x2eaebd6d46827e5bb1fd5c1a96c85f5dfbf3b77df03627545594e695867348a + 0x45bab8945c7ebe23192a98a496e1f13929ca8fc8edaf810212f0ee00aab9d1c, + 0x17f03434193b85c7f24354e7de98e4a4bc1e5bd9d26021be3cb26b9c80b282c ); // we try buying after the max_validity timestamp @@ -174,9 +174,9 @@ fn test_buy_domain_altcoin_wrong_quote() { identity.mint(id1); // we check how much a domain costs - let quote = Wad { val: 5221805004292776}; + let quote = Wad { val: 1591205338160899000000}; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; + let price_in_strk: Wad = Wad { val: price_in_eth.low } * quote; // we allow the naming to take our money strk.approve(naming.contract_address, price_in_strk.into()); @@ -184,8 +184,8 @@ fn test_buy_domain_altcoin_wrong_quote() { // we buy with no resolver, no sponsor, no discount and empty metadata let max_validity = 1000; let sig = ( - 0x2d46882b7601332cab0b45a44c5da71d7cb8698d2aaa3eee1c777430047b4b1, - 0x2eaebd6d46827e5bb1fd5c1a96c85f5dfbf3b77df03627545594e695867348a + 0x45bab8945c7ebe23192a98a496e1f13929ca8fc8edaf810212f0ee00aab9d1c, + 0x17f03434193b85c7f24354e7de98e4a4bc1e5bd9d26021be3cb26b9c80b282c ); // we try buying with a quote lower than the actual price let lower_quote = Wad { val: 1}; @@ -225,9 +225,9 @@ fn test_renew_domain_with_strk() { identity.mint(id1); // we check how much a domain costs - let quote = Wad { val: 5221805004292776}; + let quote = Wad { val: 1591205338160899000000}; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; + let price_in_strk: Wad = Wad { val: price_in_eth.low } * quote; // we allow the naming to take our money strk.approve(naming.contract_address, price_in_strk.into()); @@ -235,8 +235,8 @@ fn test_renew_domain_with_strk() { // we buy with no resolver, no sponsor, no discount and empty metadata let max_validity = 1000; let sig = ( - 0x2d46882b7601332cab0b45a44c5da71d7cb8698d2aaa3eee1c777430047b4b1, - 0x2eaebd6d46827e5bb1fd5c1a96c85f5dfbf3b77df03627545594e695867348a + 0x45bab8945c7ebe23192a98a496e1f13929ca8fc8edaf810212f0ee00aab9d1c, + 0x17f03434193b85c7f24354e7de98e4a4bc1e5bd9d26021be3cb26b9c80b282c ); naming .altcoin_buy( @@ -260,9 +260,9 @@ fn test_renew_domain_with_strk() { ); // we check how much a domain costs to renew - let quote = Wad { val: 1221805004292776}; + let quote = Wad { val: 591205338160899000000}; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; + let price_in_strk: Wad = Wad { val: price_in_eth.low } * quote; // we allow the naming to take our money strk.approve(naming.contract_address, price_in_strk.into()); @@ -270,8 +270,8 @@ fn test_renew_domain_with_strk() { // we renew with no sponsor, no discount and empty metadata let max_validity = 1000; let sig = ( - 0x42768490cdba55ef41ac540caab9a9ec4133b5d1f42289d2c32f5c1efc07f65, - 0x15d56a36d5fa94dc183ef32f4f9bc3d7f0d4b68b8b07a4541cad11a8c9cf7f6 + 0x21e23b2bf772d9c088d99103daf233d279e08fd0cce6cd079c1daec5e8e0e99, + 0x7b362f5fa5907fb805018de4361d42e887f62473d8fd84e0b207e4a9bc99aaa ); naming .altcoin_renew( @@ -336,9 +336,9 @@ fn test_subscription_with_strk() { identity.mint(id1); // we check how much a domain costs - let quote = Wad { val: 5221805004292776}; + let quote = Wad { val: 1591205338160899000000}; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; + let price_in_strk: Wad = Wad { val: price_in_eth.low } * quote; // we allow the naming to take our money strk.approve(naming.contract_address, price_in_strk.into()); @@ -346,8 +346,8 @@ fn test_subscription_with_strk() { // we buy with no resolver, no sponsor, no discount and empty metadata let max_validity = 1000; let sig = ( - 0x2d46882b7601332cab0b45a44c5da71d7cb8698d2aaa3eee1c777430047b4b1, - 0x2eaebd6d46827e5bb1fd5c1a96c85f5dfbf3b77df03627545594e695867348a + 0x45bab8945c7ebe23192a98a496e1f13929ca8fc8edaf810212f0ee00aab9d1c, + 0x17f03434193b85c7f24354e7de98e4a4bc1e5bd9d26021be3cb26b9c80b282c ); naming .altcoin_buy( @@ -371,9 +371,9 @@ fn test_subscription_with_strk() { ); // we check how much a domain costs to renew - let quote = Wad { val: 1221805004292776}; + let quote = Wad { val: 591205338160899000000}; let (_, price_in_eth) = pricing.compute_buy_price(7, 365); - let price_in_strk: Wad = Wad { val: price_in_eth.low } / quote; + let price_in_strk: Wad = Wad { val: price_in_eth.low } * quote; // we whitelist renewal contract let renewal_contract = contract_address_const::<0x456>();