@@ -34,7 +34,7 @@ import {
3434import { validateAddress , validateUrl , validatePositiveAmount } from './utils/validation.js' ;
3535import { resolveAddresses , requireAddress } from './utils/addresses.js' ;
3636import { withRetry } from './utils/retry.js' ;
37- import { AzethFactoryAbi , PaymentAgreementModuleAbi } from '@azeth/common/abis' ;
37+ import { AzethFactoryAbi , PaymentAgreementModuleAbi , TrustRegistryModuleAbi } from '@azeth/common/abis' ;
3838import { createAccount , getAccountAddress , type CreateAccountParams , type CreateAccountResult } from './account/create.js' ;
3939import { setTokenWhitelist as setTokenWhitelistFn , setProtocolWhitelist as setProtocolWhitelistFn } from './account/guardian.js' ;
4040import { getBalance , getAllBalances , type BalanceResult } from './account/balance.js' ;
@@ -103,6 +103,63 @@ export interface AzethKitConfig {
103103 guardianAutoSign ?: boolean ;
104104}
105105
106+ /** Simplified account creation — auto-fills owner, guardrails, and wraps into registry.
107+ * Use this when you want sensible defaults without manual guardrail configuration. */
108+ export interface SimpleCreateAccountParams {
109+ /** Display name for the trust registry entry */
110+ name : string ;
111+ /** Entity type: 'agent', 'service', or 'infrastructure' */
112+ entityType : 'agent' | 'service' | 'infrastructure' ;
113+ /** Human-readable description */
114+ description : string ;
115+ /** Service capabilities for discovery (e.g., ['weather-data', 'price-feed']) */
116+ capabilities ?: string [ ] ;
117+ /** Service endpoint URL */
118+ endpoint ?: string ;
119+ /** Max per-transaction amount in USD (default: 100) */
120+ maxTxAmountUSD ?: number ;
121+ /** Daily spending limit in USD (default: 1000) */
122+ dailySpendLimitUSD ?: number ;
123+ /** Guardian address for co-signing high-value txs (default: self-guardian) */
124+ guardian ?: `0x${string } `;
125+ /** Emergency withdrawal destination (default: owner) */
126+ emergencyWithdrawTo ?: `0x${string } `;
127+ }
128+
129+ /** Simplified opinion — rating from -100 to 100, auto-converts to WAD format */
130+ export interface SimpleOpinion {
131+ /** Target service's ERC-8004 token ID */
132+ serviceTokenId : bigint ;
133+ /** Rating from -100 to 100 (supports decimals like 85.5) */
134+ rating : number ;
135+ /** Primary categorization tag (default: 'quality') */
136+ tag1 ?: string ;
137+ /** Secondary categorization tag (default: '') */
138+ tag2 ?: string ;
139+ /** Service endpoint being rated */
140+ endpoint ?: string ;
141+ }
142+
143+ /** Result from pay() — fetch402 result with auto-parsed response body */
144+ export interface PayResult {
145+ /** Parsed response body (JSON object or raw text string) */
146+ data : unknown ;
147+ /** Raw HTTP response (body already consumed) */
148+ response : Response ;
149+ /** Whether an x402 payment was made */
150+ paymentMade : boolean ;
151+ /** Payment amount in token base units (e.g., USDC with 6 decimals) */
152+ amount ?: bigint ;
153+ /** On-chain transaction hash of the payment */
154+ txHash ?: `0x${string } `;
155+ /** Response time in milliseconds */
156+ responseTimeMs ?: number ;
157+ /** Whether on-chain settlement was verified */
158+ settlementVerified : boolean ;
159+ /** How access was obtained */
160+ paymentMethod : 'x402' | 'smart-account' | 'session' | 'none' ;
161+ }
162+
106163/** AzethKit -- Trust Infrastructure SDK for the Machine Economy
107164 *
108165 * Provides Phase 0 methods for machine participants:
@@ -359,10 +416,60 @@ export class AzethKit {
359416 *
360417 * Single atomic transaction: deploys ERC-1967 proxy, installs all 4 modules,
361418 * registers on ERC-8004 trust registry (optional), and permanently revokes factory access.
419+ *
420+ * Accepts either full params (CreateAccountParams) or simplified params
421+ * (SimpleCreateAccountParams) that auto-fill owner, guardrails, and registry.
422+ *
423+ * @example Simplified (recommended for most cases):
424+ * ```typescript
425+ * await agent.createAccount({
426+ * name: 'WeatherOracle',
427+ * entityType: 'service',
428+ * description: 'Real-time weather data',
429+ * capabilities: ['weather-data'],
430+ * endpoint: 'http://localhost:3402',
431+ * });
432+ * ```
433+ *
434+ * @example Full control:
435+ * ```typescript
436+ * await agent.createAccount({
437+ * owner: agent.address,
438+ * guardrails: { maxTxAmountUSD: 500n * 10n**18n, ... },
439+ * registry: { name: 'WeatherOracle', entityType: 'service', ... },
440+ * });
441+ * ```
362442 */
363- async createAccount ( params : CreateAccountParams ) : Promise < CreateAccountResult > {
443+ async createAccount ( params : CreateAccountParams | SimpleCreateAccountParams ) : Promise < CreateAccountResult > {
364444 this . _requireNotDestroyed ( ) ;
365- const result = await createAccount ( this . publicClient , this . walletClient , this . addresses , params ) ;
445+
446+ let fullParams : CreateAccountParams ;
447+ if ( 'owner' in params ) {
448+ fullParams = params ;
449+ } else {
450+ const maxTx = params . maxTxAmountUSD ?? 100 ;
451+ const dailyLimit = params . dailySpendLimitUSD ?? 1000 ;
452+ fullParams = {
453+ owner : this . address ,
454+ guardrails : {
455+ maxTxAmountUSD : BigInt ( Math . round ( maxTx ) ) * 10n ** 18n ,
456+ dailySpendLimitUSD : BigInt ( Math . round ( dailyLimit ) ) * 10n ** 18n ,
457+ guardianMaxTxAmountUSD : BigInt ( Math . round ( maxTx * 5 ) ) * 10n ** 18n ,
458+ guardianDailySpendLimitUSD : BigInt ( Math . round ( dailyLimit * 5 ) ) * 10n ** 18n ,
459+ guardian : params . guardian ?? this . address ,
460+ emergencyWithdrawTo : params . emergencyWithdrawTo ?? this . address ,
461+ } ,
462+ registry : {
463+ name : params . name ,
464+ description : params . description ,
465+ entityType : params . entityType ,
466+ capabilities : params . capabilities ?? [ ] ,
467+ endpoint : params . endpoint ,
468+ } ,
469+ } ;
470+ }
471+
472+ const result = await createAccount ( this . publicClient , this . walletClient , this . addresses , fullParams ) ;
366473 // Cache the newly created account
367474 if ( ! this . _smartAccounts ) {
368475 this . _smartAccounts = [ result . account ] ;
@@ -853,15 +960,83 @@ export class AzethKit {
853960 }
854961 }
855962
963+ /** Pay for an x402-gated service and return parsed response data.
964+ *
965+ * Convenience wrapper around fetch402 that auto-parses the response body.
966+ * Returns JSON objects for JSON responses, raw text for others.
967+ *
968+ * @param url - Service URL to fetch and pay for
969+ * @param options - Fetch options (method, body, maxAmount, etc.)
970+ * @returns PayResult with parsed `data` field
971+ *
972+ * @example
973+ * ```typescript
974+ * const result = await agent.pay('https://api.example.com/data');
975+ * console.log(result.data); // parsed JSON response
976+ * ```
977+ */
978+ async pay ( url : string , options ?: Fetch402Options ) : Promise < PayResult > {
979+ const result = await this . fetch402 ( url , options ) ;
980+ const text = await result . response . text ( ) ;
981+ let data : unknown ;
982+ try {
983+ data = JSON . parse ( text ) ;
984+ } catch {
985+ data = text ;
986+ }
987+ return { ...result , data } ;
988+ }
989+
856990 // ──────────────────────────────────────────────
857991 // Trust registry
858992 // ──────────────────────────────────────────────
859993
860- /** Register this account on the ERC-8004 trust registry */
994+ /** Register on the ERC-8004 trust registry, or update metadata if already registered.
995+ *
996+ * If the smart account is already registered (e.g., via createAccount with registry params),
997+ * this method gracefully falls back to updating the metadata fields instead of reverting.
998+ */
861999 async publishService ( params : RegisterParams ) : Promise < RegisterResult > {
8621000 this . _requireNotDestroyed ( ) ;
8631001 const account = await this . resolveSmartAccount ( ) ;
8641002 const smartAccountClient = await this . _getSmartAccountClient ( account ) ;
1003+
1004+ // Check if already registered to avoid AlreadyRegistered revert
1005+ const moduleAddress = this . addresses . trustRegistryModule ;
1006+ if ( moduleAddress ) {
1007+ let isRegistered = false ;
1008+ try {
1009+ isRegistered = await this . publicClient . readContract ( {
1010+ address : moduleAddress ,
1011+ abi : TrustRegistryModuleAbi ,
1012+ functionName : 'isRegistered' ,
1013+ args : [ account ] ,
1014+ } ) as boolean ;
1015+ } catch {
1016+ // If check fails, proceed with registration attempt
1017+ }
1018+
1019+ if ( isRegistered ) {
1020+ // Already registered — update metadata instead
1021+ const updates : MetadataUpdate [ ] = [ ] ;
1022+ if ( params . name ) updates . push ( { key : 'name' , value : params . name } ) ;
1023+ if ( params . description ) updates . push ( { key : 'description' , value : params . description } ) ;
1024+ if ( params . capabilities . length > 0 ) {
1025+ updates . push ( { key : 'capabilities' , value : JSON . stringify ( params . capabilities ) } ) ;
1026+ }
1027+ if ( params . endpoint ) updates . push ( { key : 'endpoint' , value : params . endpoint } ) ;
1028+
1029+ let txHash = '0x' as `0x${string } `;
1030+ if ( updates . length > 0 ) {
1031+ txHash = await updateMetadataBatch (
1032+ this . publicClient , smartAccountClient , this . addresses , this . address , updates ,
1033+ ) ;
1034+ }
1035+
1036+ return { tokenId : 0n , txHash } ;
1037+ }
1038+ }
1039+
8651040 return registerOnRegistry (
8661041 this . publicClient , smartAccountClient , this . addresses , this . address , params ,
8671042 ) ;
@@ -1050,15 +1225,55 @@ export class AzethKit {
10501225
10511226 /** Submit a reputation opinion for an agent via the ReputationModule.
10521227 *
1053- * Requires a positive net USD payment from this account to the target agent
1054- * (aggregated on-chain via Chainlink). Value is int128 with configurable decimal precision.
1228+ * Accepts either full OnChainOpinion params or simplified SimpleOpinion with a rating.
1229+ * Requires a positive net USD payment from this account to the target agent.
1230+ *
1231+ * @example Simplified:
1232+ * ```typescript
1233+ * await agent.submitOpinion({
1234+ * serviceTokenId: service.tokenId,
1235+ * rating: 85, // -100 to 100
1236+ * tag1: 'quality', // optional
1237+ * });
1238+ * ```
1239+ *
1240+ * @example Full control:
1241+ * ```typescript
1242+ * await agent.submitOpinion({
1243+ * agentId: 1024n,
1244+ * value: 85n * 10n**18n,
1245+ * valueDecimals: 18,
1246+ * tag1: 'quality', tag2: 'x402',
1247+ * endpoint: 'https://...', opinionURI: '', opinionHash: '0x...',
1248+ * });
1249+ * ```
10551250 */
1056- async submitOpinion ( opinion : OnChainOpinion ) : Promise < `0x${string } `> {
1251+ async submitOpinion ( opinion : OnChainOpinion | SimpleOpinion ) : Promise < `0x${string } `> {
10571252 this . _requireNotDestroyed ( ) ;
1253+
1254+ let fullOpinion : OnChainOpinion ;
1255+ if ( 'agentId' in opinion ) {
1256+ fullOpinion = opinion ;
1257+ } else {
1258+ if ( opinion . rating < - 100 || opinion . rating > 100 ) {
1259+ throw new AzethError ( 'Rating must be between -100 and 100' , 'INVALID_INPUT' , { field : 'rating' } ) ;
1260+ }
1261+ fullOpinion = {
1262+ agentId : opinion . serviceTokenId ,
1263+ value : BigInt ( Math . round ( opinion . rating * 1e18 ) ) ,
1264+ valueDecimals : 18 ,
1265+ tag1 : opinion . tag1 ?? 'quality' ,
1266+ tag2 : opinion . tag2 ?? '' ,
1267+ endpoint : opinion . endpoint ?? '' ,
1268+ opinionURI : '' ,
1269+ opinionHash : '0x0000000000000000000000000000000000000000000000000000000000000000' as `0x${string } `,
1270+ } ;
1271+ }
1272+
10581273 const account = await this . resolveSmartAccount ( ) ;
10591274 const smartAccountClient = await this . _getSmartAccountClient ( account ) ;
10601275 return submitOnChainOpinion (
1061- this . publicClient , smartAccountClient , this . addresses , this . address , opinion ,
1276+ this . publicClient , smartAccountClient , this . addresses , this . address , fullOpinion ,
10621277 ) ;
10631278 }
10641279
0 commit comments