diff --git a/packages/dart/CHANGELOG.md b/packages/dart/CHANGELOG.md index 366d917..51da49b 100644 --- a/packages/dart/CHANGELOG.md +++ b/packages/dart/CHANGELOG.md @@ -4,7 +4,15 @@ All notable changes to this package are documented here. Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning follows [SemVer](https://semver.org/spec/v2.0.0.html). -## 0.2.1-beta +## 0.1.1-beta + +### Changed + +- Pin the deployed SafHandle mainnet contract address on `safrochain-1`. +- Point the mainnet preset at `rpc/api.safrochain.network` and set the + gas price to `0.15usaf`. + +## 0.1.0 ### Added diff --git a/packages/dart/README.md b/packages/dart/README.md index d0dcb5b..e60af40 100644 --- a/packages/dart/README.md +++ b/packages/dart/README.md @@ -23,7 +23,7 @@ rules, and message shapes match one-to-one. ```yaml # pubspec.yaml dependencies: - safhandle: ^0.2.1-beta + safhandle: ^0.1.0 ``` ```bash diff --git a/packages/dart/example/resolve.dart b/packages/dart/example/resolve.dart index df558d7..27b19db 100644 --- a/packages/dart/example/resolve.dart +++ b/packages/dart/example/resolve.dart @@ -30,7 +30,8 @@ Future main(List args) async { final record = await client.getAddress(input); stdout.writeln('$input → $address'); - stdout.writeln(' type: ${record.recordType}, key: ${record.normalizedKey}'); + stdout + .writeln(' type: ${record.recordType}, key: ${record.normalizedKey}'); } finally { client.close(); } diff --git a/packages/dart/lib/src/client.dart b/packages/dart/lib/src/client.dart index 2b86a47..89c49a2 100644 --- a/packages/dart/lib/src/client.dart +++ b/packages/dart/lib/src/client.dart @@ -78,7 +78,8 @@ class SafHandleClient { late final http.Response res; try { - res = await _http.get(uri, headers: const {'Accept': 'application/json'}); + res = await _http.get(uri, + headers: const {'Accept': 'application/json'}); } catch (err) { throw SafHandleQueryException('Request failed: $err'); } @@ -127,7 +128,8 @@ class SafHandleClient { /// Throws when the handle is unregistered (see [lookup] for a null-returning /// variant). Future getAddress(String input) async { - return GetAddressResponse.fromJson(await _queryObject(QueryMsg.getAddress(input))); + return GetAddressResponse.fromJson( + await _queryObject(QueryMsg.getAddress(input))); } /// Resolve a name, returning just the address, or `null` if the handle is not @@ -144,7 +146,8 @@ class SafHandleClient { /// Resolve by name only. [name] may be bare (`john`) or full (`john.saf`). Future resolveName(String name) async { - final res = AddressResponse.fromJson(await _queryObject(QueryMsg.resolveName(name))); + final res = AddressResponse.fromJson( + await _queryObject(QueryMsg.resolveName(name))); return res.address; } @@ -152,7 +155,8 @@ class SafHandleClient { /// record — [HandlesResponse.name] comes back as `null` when the address owns /// none. Future getHandles(String address) async { - return HandlesResponse.fromJson(await _queryObject(QueryMsg.handles(address))); + return HandlesResponse.fromJson( + await _queryObject(QueryMsg.handles(address))); } /// Full record for a name, including owner and registration height. diff --git a/packages/dart/lib/src/constants.dart b/packages/dart/lib/src/constants.dart index bc37ae9..919818c 100644 --- a/packages/dart/lib/src/constants.dart +++ b/packages/dart/lib/src/constants.dart @@ -43,13 +43,12 @@ class NetworkConfig { /// Deployed SafHandle contract address per named network, baked into the SDK so /// callers don't have to carry it around. -/// -/// `mainnet` is a placeholder — swap it for the real address once the contract -/// is deployed and the SDK is verified against it. -const Map contractAddresses = { +const Map contractAddresses = + { SafHandleNetwork.testnet: 'addr_safro17ykz5k26fzg0808knarpgg2tdxxxfmefuh9mydusqyfkr3vtdvysvv2vs0', - SafHandleNetwork.mainnet: 'addr_saf_MAIN_NET', + SafHandleNetwork.mainnet: + 'addr_safro14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s0nv26n', }; /// Safrochain testnet (`safro-testnet-1`) preset. @@ -65,16 +64,17 @@ const NetworkConfig safrochainTestnet = NetworkConfig( 'addr_safro17ykz5k26fzg0808knarpgg2tdxxxfmefuh9mydusqyfkr3vtdvysvv2vs0', ); -/// Safrochain mainnet (`safrochain-1`) preset. Contract address is a placeholder -/// until the contract is deployed. +/// Safrochain mainnet (`safrochain-1`) preset. const NetworkConfig safrochainMainnet = NetworkConfig( chainId: 'safrochain-1', - rpcEndpoint: 'https://rpc.safrochain.com', - restEndpoint: 'https://api.safrochain.com', + rpcEndpoint: 'https://rpc.safrochain.network', + restEndpoint: 'https://api.safrochain.network', addressPrefix: 'addr_safro', denom: 'usaf', - gasPrice: '100000usaf', - contractAddress: 'addr_saf_MAIN_NET', + gasPrice: '0.15usaf', + // SafHandle v1 (name-only) contract on safrochain-1. + contractAddress: + 'addr_safro14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s0nv26n', ); /// Default gas price used when constructing a signing client. diff --git a/packages/dart/lib/src/signing_client.dart b/packages/dart/lib/src/signing_client.dart index e637b1f..bde22f7 100644 --- a/packages/dart/lib/src/signing_client.dart +++ b/packages/dart/lib/src/signing_client.dart @@ -9,7 +9,8 @@ import 'validation.dart'; /// Result of a signed, broadcast execute call. @immutable class ExecuteResult { - const ExecuteResult({required this.transactionHash, this.height, this.rawLog}); + const ExecuteResult( + {required this.transactionHash, this.height, this.rawLog}); final String transactionHash; final int? height; @@ -117,7 +118,8 @@ class SafHandleSigningClient extends SafHandleClient { /// Register a short name for the sender. Validates and normalizes locally, /// attaches the exact registration fee (from [WriteOptions.fee], else fetched /// from the contract config). - Future registerName(String name, {WriteOptions? options}) async { + Future registerName(String name, + {WriteOptions? options}) async { final normalized = normalizeName(name); final fee = options?.fee ?? await _nameFee(); return _exec( diff --git a/packages/dart/lib/src/types.dart b/packages/dart/lib/src/types.dart index b1c09d3..dbb7818 100644 --- a/packages/dart/lib/src/types.dart +++ b/packages/dart/lib/src/types.dart @@ -125,7 +125,8 @@ class Coin { final String denom; final String amount; - Map toJson() => {'denom': denom, 'amount': amount}; + Map toJson() => + {'denom': denom, 'amount': amount}; @override bool operator ==(Object other) => diff --git a/packages/dart/lib/src/validation.dart b/packages/dart/lib/src/validation.dart index 6d95c44..7736dcd 100644 --- a/packages/dart/lib/src/validation.dart +++ b/packages/dart/lib/src/validation.dart @@ -46,10 +46,12 @@ bool _isAscii(String s) { /// `reservedName`. String normalizeName(String input) { final trimmed = input.trim(); - final bare = - trimmed.startsWith(namePrefix) ? trimmed.substring(namePrefix.length) : trimmed; + final bare = trimmed.startsWith(namePrefix) + ? trimmed.substring(namePrefix.length) + : trimmed; if (bare.isEmpty) { - throw const SafHandleError(SafHandleErrorCode.invalidName, 'Name is empty.'); + throw const SafHandleError( + SafHandleErrorCode.invalidName, 'Name is empty.'); } // Any `@` beyond the leading display marker means an email-shaped input. @@ -68,8 +70,9 @@ String normalizeName(String input) { } final lowered = bare.toLowerCase(); - final label = - lowered.endsWith(nameSuffix) ? lowered.substring(0, lowered.length - nameSuffix.length) : lowered; + final label = lowered.endsWith(nameSuffix) + ? lowered.substring(0, lowered.length - nameSuffix.length) + : lowered; // After stripping the single allowed `.saf` suffix, no dot may remain // (blocks `john.com`, `a.b.saf`, and other domain/email-shaped input). diff --git a/packages/dart/pubspec.yaml b/packages/dart/pubspec.yaml index 6b2a2f8..bc88bea 100644 --- a/packages/dart/pubspec.yaml +++ b/packages/dart/pubspec.yaml @@ -1,9 +1,9 @@ name: safhandle description: >- - Dart/Flutter SDK for the SafHandle name service on Safrochain — resolve wallet - addresses by name and register handles on-chain, with client-side validation - that mirrors the contract. -version: 0.2.1-beta + Dart/Flutter SDK for the SafHandle name service on Safrochain — resolve + wallet addresses by name and register handles on-chain, mirroring the + contract's validation. +version: 0.1.1-beta repository: https://github.com/Safrochain-Org/safhandle-sdk homepage: https://github.com/Safrochain-Org/safhandle-sdk/tree/main/packages/dart issue_tracker: https://github.com/Safrochain-Org/safhandle-sdk/issues diff --git a/packages/dart/test/client_test.dart b/packages/dart/test/client_test.dart index a5e5c27..ce2155c 100644 --- a/packages/dart/test/client_test.dart +++ b/packages/dart/test/client_test.dart @@ -24,11 +24,13 @@ SafHandleClient clientWith(MockClient mock) => SafHandleClient( ); void main() { - test('getAddress builds a get_address smart query and parses the response', () async { + test('getAddress builds a get_address smart query and parses the response', + () async { late Map seenQuery; final mock = MockClient((request) async { seenQuery = decodeQuery(request); - expect(request.url.path, contains('/cosmwasm/wasm/v1/contract/$contract/smart/')); + expect(request.url.path, + contains('/cosmwasm/wasm/v1/contract/$contract/smart/')); return http.Response( jsonEncode({ 'data': { @@ -82,7 +84,9 @@ void main() { test('getHandles parses a null name (address owns nothing)', () async { final mock = MockClient((request) async { return http.Response( - jsonEncode({'data': {'name': null}}), + jsonEncode({ + 'data': {'name': null} + }), 200, ); }); diff --git a/packages/dart/test/signing_client_test.dart b/packages/dart/test/signing_client_test.dart index 4e328fd..363a036 100644 --- a/packages/dart/test/signing_client_test.dart +++ b/packages/dart/test/signing_client_test.dart @@ -56,8 +56,9 @@ class RecordingSigner implements SafHandleSigner { // Path: /cosmwasm/wasm/v1/contract/{addr}/smart/{b64} final segments = request.url.pathSegments; final encoded = segments.last; - final decoded = jsonDecode(utf8.decode(base64.decode(Uri.decodeComponent(encoded)))) - as Map; + final decoded = + jsonDecode(utf8.decode(base64.decode(Uri.decodeComponent(encoded)))) + as Map; if (decoded.containsKey('config')) { configQueries++; return http.Response(jsonEncode({'data': config}), 200, @@ -68,7 +69,8 @@ class RecordingSigner implements SafHandleSigner { return (client: client, configQueries: () => configQueries); } -SafHandleSigningClient makeClient(RecordingSigner signer, http.Client httpClient) { +SafHandleSigningClient makeClient( + RecordingSigner signer, http.Client httpClient) { return SafHandleSigningClient( signer: signer, restEndpoint: 'https://rest.example', @@ -79,7 +81,8 @@ SafHandleSigningClient makeClient(RecordingSigner signer, http.Client httpClient void main() { group('registerName', () { - test('normalizes the name and attaches the exact fee from config', () async { + test('normalizes the name and attaches the exact fee from config', + () async { final signer = RecordingSigner(); final rest = fakeRest(); final client = makeClient(signer, rest.client); @@ -128,7 +131,8 @@ void main() { }); group('transfer / release (no funds)', () { - test('transferName sends normalized name and new owner with no funds', () async { + test('transferName sends normalized name and new owner with no funds', + () async { final signer = RecordingSigner(); final client = makeClient(signer, fakeRest().client); diff --git a/packages/dart/test/validation_test.dart b/packages/dart/test/validation_test.dart index 87e63a3..423a495 100644 --- a/packages/dart/test/validation_test.dart +++ b/packages/dart/test/validation_test.dart @@ -5,7 +5,8 @@ import 'package:test/test.dart'; // contract's validation.rs tests. If these drift from the contract, // registrations built by the SDK would be rejected on-chain — so parity matters. -const addr = 'addr_safro10fmuatrxlcj6644vang5fuwyvdldfjss4tqqvemw9upm0qpn54esxr94v2'; +const addr = + 'addr_safro10fmuatrxlcj6644vang5fuwyvdldfjss4tqqvemw9upm0qpn54esxr94v2'; void main() { group('normalizeName', () { @@ -109,16 +110,18 @@ void main() { group('parseInput', () { test("routes '@name' to the name lane, normalized", () { - expect(parseInput('@john'), const ParsedInput(HandleKind.name, 'john.saf')); - expect(parseInput(' @Alice '), const ParsedInput(HandleKind.name, 'alice.saf')); + expect( + parseInput('@john'), const ParsedInput(HandleKind.name, 'john.saf')); + expect(parseInput(' @Alice '), + const ParsedInput(HandleKind.name, 'alice.saf')); }); test('rejects an all-digit input as invalidInput', () { for (final input in ['243899123456', '123']) { expect( () => parseInput(input), - throwsA(isA().having( - (e) => e.code, 'code', SafHandleErrorCode.invalidInput)), + throwsA(isA() + .having((e) => e.code, 'code', SafHandleErrorCode.invalidInput)), reason: 'input: $input', ); }