+1
-1
package.json
+1
-1
package.json
+3
-3
pnpm-lock.yaml
+3
-3
pnpm-lock.yaml
···
14
14
'@ethereum-attestation-service/eas-sdk':
15
15
specifier: ^2.9.0
16
16
version: 2.9.0(typescript@5.9.3)(zod@3.25.76)
17
-
viem:
18
-
specifier: ^2.38.0
19
-
version: 2.38.0(typescript@5.9.3)(zod@3.25.76)
17
+
ethers:
18
+
specifier: ^6.15.0
19
+
version: 6.15.0
20
20
devDependencies:
21
21
'@types/node':
22
22
specifier: ^20.19.21
+25
-17
src/cli.ts
+25
-17
src/cli.ts
···
66
66
67
67
spinner.succeed('Initialized');
68
68
69
-
spinner.start('Fetching record from AT Protocol...');
70
-
const record = await notary.fetchRecord(recordURI);
71
-
spinner.succeed('Record fetched');
69
+
spinner.start('Resolving DID to PDS...');
70
+
const { record, pds } = await notary.fetchRecord(recordURI);
71
+
spinner.succeed(`Record fetched from PDS: ${pds}`);
72
72
73
73
console.log(chalk.gray('\nRecord preview:'));
74
74
console.log(chalk.gray(JSON.stringify(record.value, null, 2).substring(0, 200) + '...\n'));
75
75
76
-
spinner.start('Creating attestation...');
76
+
spinner.start('Creating attestation on Ethereum...');
77
77
const result = await notary.notarizeRecord(recordURI);
78
78
spinner.succeed('Attestation created!');
79
79
80
80
console.log(chalk.green('\n✅ Notarization Complete!\n'));
81
81
console.log(chalk.blue('Attestation UID:'), chalk.cyan(result.attestationUID));
82
82
console.log(chalk.blue('Record URI:'), chalk.gray(result.recordURI));
83
+
console.log(chalk.blue('CID:'), chalk.gray(result.cid));
83
84
console.log(chalk.blue('Content Hash:'), chalk.gray(result.contentHash));
84
-
console.log(chalk.blue('Lexicon:'), chalk.gray(result.lexicon));
85
+
console.log(chalk.blue('PDS:'), chalk.gray(result.pds));
85
86
console.log(chalk.blue('Transaction:'), chalk.gray(result.transactionHash));
86
87
87
88
console.log(chalk.yellow('\n📋 View on EAS Explorer:'));
···
96
97
}
97
98
});
98
99
100
+
99
101
// Verify command
100
102
program
101
103
.command('verify <attestationUID>')
···
119
121
console.log(chalk.green('\n✅ Attestation Valid\n'));
120
122
console.log(chalk.blue('UID:'), chalk.cyan(attestation.uid));
121
123
console.log(chalk.blue('Record URI:'), chalk.gray(attestation.recordURI));
124
+
console.log(chalk.blue('CID:'), chalk.gray(attestation.cid));
122
125
console.log(chalk.blue('Content Hash:'), chalk.gray(attestation.contentHash));
123
-
console.log(chalk.blue('Lexicon:'), chalk.gray(attestation.lexicon));
126
+
console.log(chalk.blue('PDS:'), chalk.gray(attestation.pds));
124
127
console.log(chalk.blue('Timestamp:'), chalk.gray(new Date(attestation.timestamp * 1000).toISOString()));
125
128
console.log(chalk.blue('Attester:'), chalk.gray(attestation.attester));
126
-
console.log(chalk.blue('Revoked:'), attestation.revoked ? chalk.red('Yes') : chalk.green('No'));
127
129
128
130
// Compare with current if requested
129
131
if (options.compare) {
130
132
console.log(chalk.yellow('\n🔄 Comparing with current record...'));
131
133
132
-
const comparison = await notary.compareWithCurrent(attestation);
133
-
134
-
if (!comparison.exists) {
135
-
console.log(chalk.red('⚠ Record has been deleted'));
136
-
} else if (comparison.matches) {
137
-
console.log(chalk.green('✓ Content matches attestation (unchanged)'));
138
-
} else {
139
-
console.log(chalk.yellow('⚠ Content has changed since attestation'));
140
-
console.log(chalk.gray(` Original: ${attestation.contentHash.substring(0, 20)}...`));
141
-
console.log(chalk.gray(` Current: ${comparison.currentHash!.substring(0, 20)}...`));
134
+
try {
135
+
const comparison = await notary.compareWithCurrent(attestation);
136
+
137
+
if (!comparison.exists) {
138
+
console.log(chalk.red('⚠ Record has been deleted'));
139
+
} else if (comparison.matches) {
140
+
console.log(chalk.green('✓ Content matches attestation (unchanged)'));
141
+
} else {
142
+
console.log(chalk.yellow('⚠ Content has changed since attestation'));
143
+
console.log(chalk.gray(` Attested CID: ${attestation.cid}`));
144
+
console.log(chalk.gray(` Attested Hash: ${attestation.contentHash.substring(0, 20)}...`));
145
+
console.log(chalk.gray(` Current Hash: ${comparison.currentHash!.substring(0, 20)}...`));
146
+
}
147
+
} catch (err: any) {
148
+
console.log(chalk.red(`⚠ Could not fetch current record: ${err.message}`));
142
149
}
143
150
}
144
151
···
150
157
process.exit(1);
151
158
}
152
159
});
160
+
153
161
154
162
program.parse();
+158
-248
src/lib/notary.ts
+158
-248
src/lib/notary.ts
···
1
1
import { AtpAgent } from '@atproto/api';
2
-
import {
3
-
createPublicClient,
4
-
createWalletClient,
5
-
http,
6
-
type Address,
7
-
type Hash,
8
-
type PublicClient,
9
-
type WalletClient,
10
-
zeroAddress,
11
-
encodeAbiParameters,
12
-
parseAbiParameters,
13
-
decodeAbiParameters,
14
-
keccak256,
15
-
toHex,
16
-
} from 'viem';
17
-
import { privateKeyToAccount } from 'viem/accounts';
18
-
import { sepolia, base, baseSepolia } from 'viem/chains';
2
+
import { ethers } from 'ethers';
3
+
import EASPackage from '@ethereum-attestation-service/eas-sdk';
4
+
const { EAS, SchemaEncoder, SchemaRegistry, NO_EXPIRATION } = EASPackage;
5
+
19
6
import type { NotaryConfig, NotarizationResult, AttestationData } from './types';
20
7
import { parseRecordURI, hashContent, getExplorerURL } from './utils';
21
8
22
9
// Chain configurations
23
10
const CHAIN_CONFIG = {
24
11
'sepolia': {
25
-
chain: sepolia,
26
12
rpcUrl: 'https://1rpc.io/sepolia',
27
-
easContractAddress: '0xC2679fBD37d54388Ce493F1DB75320D236e1815e' as Address,
28
-
schemaRegistryAddress: '0x0a7E2Ff54e76B8E6659aedc9103FB21c038050D0' as Address,
13
+
easContractAddress: '0xC2679fBD37d54388Ce493F1DB75320D236e1815e',
14
+
schemaRegistryAddress: '0x0a7E2Ff54e76B8E6659aedc9103FB21c038050D0',
29
15
},
30
16
'base-sepolia': {
31
-
chain: baseSepolia,
32
17
rpcUrl: 'https://sepolia.base.org',
33
-
easContractAddress: '0x4200000000000000000000000000000000000021' as Address,
34
-
schemaRegistryAddress: '0x4200000000000000000000000000000000000020' as Address,
18
+
easContractAddress: '0x4200000000000000000000000000000000000021',
19
+
schemaRegistryAddress: '0x4200000000000000000000000000000000000020',
35
20
},
36
21
'base': {
37
-
chain: base,
38
22
rpcUrl: 'https://mainnet.base.org',
39
-
easContractAddress: '0x4200000000000000000000000000000000000021' as Address,
40
-
schemaRegistryAddress: '0x4200000000000000000000000000000000000020' as Address,
23
+
easContractAddress: '0x4200000000000000000000000000000000000021',
24
+
schemaRegistryAddress: '0x4200000000000000000000000000000000000020',
41
25
}
42
26
};
43
27
44
-
const SCHEMA_STRING = "string recordURI,bytes32 contentHash,string lexicon,uint256 timestamp";
45
-
46
-
// EAS Contract ABIs
47
-
const SCHEMA_REGISTRY_ABI = [
48
-
{
49
-
type: 'function',
50
-
name: 'register',
51
-
inputs: [
52
-
{ name: 'schema', type: 'string' },
53
-
{ name: 'resolver', type: 'address' },
54
-
{ name: 'revocable', type: 'bool' }
55
-
],
56
-
outputs: [{ name: 'uid', type: 'bytes32' }],
57
-
stateMutability: 'nonpayable',
58
-
},
59
-
] as const;
60
-
61
-
const EAS_ABI = [
62
-
{
63
-
type: 'function',
64
-
name: 'attest',
65
-
inputs: [
66
-
{
67
-
name: 'request',
68
-
type: 'tuple',
69
-
components: [
70
-
{ name: 'schema', type: 'bytes32' },
71
-
{
72
-
name: 'data',
73
-
type: 'tuple',
74
-
components: [
75
-
{ name: 'recipient', type: 'address' },
76
-
{ name: 'expirationTime', type: 'uint64' },
77
-
{ name: 'revocable', type: 'bool' },
78
-
{ name: 'refUID', type: 'bytes32' },
79
-
{ name: 'data', type: 'bytes' },
80
-
{ name: 'value', type: 'uint256' },
81
-
],
82
-
},
83
-
],
84
-
},
85
-
],
86
-
outputs: [{ name: 'uid', type: 'bytes32' }],
87
-
stateMutability: 'payable',
88
-
},
89
-
{
90
-
type: 'function',
91
-
name: 'getAttestation',
92
-
inputs: [{ name: 'uid', type: 'bytes32' }],
93
-
outputs: [
94
-
{
95
-
name: 'attestation',
96
-
type: 'tuple',
97
-
components: [
98
-
{ name: 'uid', type: 'bytes32' },
99
-
{ name: 'schema', type: 'bytes32' },
100
-
{ name: 'time', type: 'uint64' },
101
-
{ name: 'expirationTime', type: 'uint64' },
102
-
{ name: 'revocationTime', type: 'uint64' },
103
-
{ name: 'refUID', type: 'bytes32' },
104
-
{ name: 'recipient', type: 'address' },
105
-
{ name: 'attester', type: 'address' },
106
-
{ name: 'revocable', type: 'bool' },
107
-
{ name: 'data', type: 'bytes' },
108
-
],
109
-
},
110
-
],
111
-
stateMutability: 'view',
112
-
},
113
-
] as const;
114
-
115
-
// Helper to encode attestation data
116
-
function encodeAttestationData(params: {
117
-
recordURI: string;
118
-
contentHash: Hash;
119
-
lexicon: string;
120
-
timestamp: number;
121
-
}): Hash {
122
-
return encodeAbiParameters(
123
-
parseAbiParameters(SCHEMA_STRING),
124
-
[params.recordURI, params.contentHash, params.lexicon, BigInt(params.timestamp)]
125
-
);
126
-
}
127
-
128
-
// Helper to decode attestation data
129
-
function decodeAttestationData(data: Hash): {
130
-
recordURI: string;
131
-
contentHash: Hash;
132
-
lexicon: string;
133
-
timestamp: number;
134
-
} {
135
-
const [recordURI, contentHash, lexicon, timestamp] = decodeAbiParameters(
136
-
parseAbiParameters(SCHEMA_STRING),
137
-
data
138
-
);
139
-
140
-
return {
141
-
recordURI: recordURI as string,
142
-
contentHash: contentHash as Hash,
143
-
lexicon: lexicon as string,
144
-
timestamp: Number(timestamp),
145
-
};
146
-
}
28
+
const SCHEMA_STRING = "string recordURI,string cid,bytes32 contentHash,string pds,uint256 timestamp";
147
29
148
30
export class ATProtocolNotary {
149
31
private config: Required<NotaryConfig>;
150
-
private publicClient: PublicClient;
151
-
private walletClient: WalletClient;
152
-
private account: ReturnType<typeof privateKeyToAccount>;
32
+
private provider: ethers.JsonRpcProvider;
33
+
private signer: ethers.Wallet;
153
34
private network: string;
154
35
private chainConfig: typeof CHAIN_CONFIG[keyof typeof CHAIN_CONFIG];
36
+
private eas: any;
37
+
private schemaRegistry: any;
155
38
156
39
constructor(config: NotaryConfig, network: string = 'sepolia') {
157
40
this.network = network;
···
160
43
this.config = {
161
44
privateKey: config.privateKey,
162
45
rpcUrl: config.rpcUrl || this.chainConfig.rpcUrl,
163
-
easContractAddress: (config.easContractAddress || this.chainConfig.easContractAddress) as Address,
164
-
schemaRegistryAddress: (config.schemaRegistryAddress || this.chainConfig.schemaRegistryAddress) as Address,
46
+
easContractAddress: config.easContractAddress || this.chainConfig.easContractAddress,
47
+
schemaRegistryAddress: config.schemaRegistryAddress || this.chainConfig.schemaRegistryAddress,
165
48
schemaUID: config.schemaUID || '',
166
49
};
167
50
···
169
52
throw new Error('Private key is required');
170
53
}
171
54
172
-
// Create account from private key
173
-
this.account = privateKeyToAccount(this.config.privateKey as Hash);
55
+
// Create ethers provider and signer
56
+
this.provider = new ethers.JsonRpcProvider(this.config.rpcUrl);
57
+
this.signer = new ethers.Wallet(this.config.privateKey, this.provider);
174
58
175
-
// Create public client (for reading)
176
-
this.publicClient = createPublicClient({
177
-
chain: this.chainConfig.chain,
178
-
transport: http(this.config.rpcUrl),
179
-
});
59
+
// Initialize EAS SDK
60
+
this.eas = new EAS(this.config.easContractAddress);
61
+
this.eas.connect(this.signer);
180
62
181
-
// Create wallet client (for writing)
182
-
this.walletClient = createWalletClient({
183
-
account: this.account,
184
-
chain: this.chainConfig.chain,
185
-
transport: http(this.config.rpcUrl),
186
-
});
63
+
this.schemaRegistry = new SchemaRegistry(this.config.schemaRegistryAddress);
64
+
this.schemaRegistry.connect(this.signer);
187
65
}
188
66
189
67
/**
190
68
* Initialize: Create EAS schema (one-time setup)
191
69
*/
192
70
async initializeSchema(): Promise<string> {
193
-
194
-
// Call register function on SchemaRegistry
195
-
const hash = await this.walletClient.writeContract({
196
-
address: this.config.schemaRegistryAddress,
197
-
abi: SCHEMA_REGISTRY_ABI,
198
-
functionName: 'register',
199
-
args: [SCHEMA_STRING, zeroAddress, true],
71
+
const transaction = await this.schemaRegistry.register({
72
+
schema: SCHEMA_STRING,
73
+
resolverAddress: ethers.ZeroAddress,
74
+
revocable: true,
200
75
});
201
76
202
-
// Wait for transaction receipt
203
-
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
77
+
await transaction.wait();
78
+
79
+
// Return the transaction hash as schema UID placeholder
80
+
return transaction.tx?.hash;
81
+
}
204
82
205
-
// Extract schema UID from logs
206
-
// The SchemaRegistered event emits the schema UID
207
-
if (receipt.logs.length > 0) {
208
-
// The first topic after the event signature is the schema UID
209
-
const schemaUID = receipt.logs[0].topics[1];
210
-
if (schemaUID) {
211
-
return schemaUID;
83
+
/**
84
+
* Resolve DID to PDS endpoint
85
+
*/
86
+
async resolveDIDtoPDS(did: string): Promise<string> {
87
+
// For did:plc, resolve via PLC directory
88
+
if (did.startsWith('did:plc:')) {
89
+
const response = await fetch(`https://plc.directory/${did}`);
90
+
if (!response.ok) {
91
+
throw new Error(`Failed to resolve DID: ${did}`);
212
92
}
93
+
94
+
const didDocument: any = await response.json();
95
+
96
+
// Find the PDS service endpoint
97
+
const pdsService = didDocument.service?.find(
98
+
(s: any) => s.id === '#atproto_pds' || s.type === 'AtprotoPersonalDataServer'
99
+
);
100
+
101
+
if (!pdsService?.serviceEndpoint) {
102
+
throw new Error(`No PDS endpoint found for DID: ${did}`);
103
+
}
104
+
105
+
return pdsService.serviceEndpoint;
213
106
}
214
-
215
-
// Fallback: return transaction hash
216
-
return hash;
107
+
108
+
// For did:web, resolve via web
109
+
if (did.startsWith('did:web:')) {
110
+
const domain = did.replace('did:web:', '');
111
+
const response = await fetch(`https://${domain}/.well-known/did.json`);
112
+
if (!response.ok) {
113
+
throw new Error(`Failed to resolve DID: ${did}`);
114
+
}
115
+
116
+
const didDocument: any = await response.json();
117
+
const pdsService = didDocument.service?.find(
118
+
(s: any) => s.id === '#atproto_pds' || s.type === 'AtprotoPersonalDataServer'
119
+
);
120
+
121
+
if (!pdsService?.serviceEndpoint) {
122
+
throw new Error(`No PDS endpoint found for DID: ${did}`);
123
+
}
124
+
125
+
return pdsService.serviceEndpoint;
126
+
}
127
+
128
+
throw new Error(`Unsupported DID method: ${did}`);
217
129
}
218
130
219
131
/**
220
-
* Fetch a record from AT Protocol
132
+
* Fetch a record from AT Protocol (directly from PDS)
221
133
*/
222
-
async fetchRecord(recordURI: string): Promise<any> {
134
+
async fetchRecord(recordURI: string): Promise<{ record: any; pds: string }> {
223
135
const { did, collection, rkey } = parseRecordURI(recordURI);
224
136
225
-
const agent = new AtpAgent({ service: 'https://public.api.bsky.app' });
137
+
// Resolve DID to PDS
138
+
const pds = await this.resolveDIDtoPDS(did);
139
+
140
+
// Create agent pointing to user's PDS
141
+
const agent = new AtpAgent({ service: pds });
226
142
227
143
const response = await agent.com.atproto.repo.getRecord({
228
144
repo: did,
···
230
146
rkey: rkey
231
147
});
232
148
233
-
return response.data;
149
+
return {
150
+
record: response.data,
151
+
pds: pds
152
+
};
234
153
}
235
154
236
-
/**
237
-
* Notarize an AT Protocol record on Ethereum
238
-
*/
239
-
async notarizeRecord(recordURI: string): Promise<NotarizationResult> {
240
-
if (!this.config.schemaUID) {
241
-
throw new Error('Schema UID not set. Run initializeSchema() first.');
242
-
}
155
+
/**
156
+
* Notarize an AT Protocol record on Ethereum
157
+
*/
158
+
async notarizeRecord(recordURI: string): Promise<NotarizationResult> {
159
+
if (!this.config.schemaUID) {
160
+
throw new Error('Schema UID not set. Run initializeSchema() first.');
161
+
}
243
162
244
-
// Parse URI
245
-
const { collection } = parseRecordURI(recordURI);
163
+
// Parse URI
164
+
const { collection } = parseRecordURI(recordURI);
246
165
247
-
// Fetch record
248
-
const record = await this.fetchRecord(recordURI);
166
+
// Fetch record directly from PDS
167
+
const { record, pds } = await this.fetchRecord(recordURI);
249
168
250
-
// Generate content hash
251
-
const contentHash = hashContent(record.value);
169
+
// Generate content hash
170
+
const contentHash = hashContent(record.value);
252
171
253
-
// Encode attestation data
254
-
const encodedData = encodeAttestationData({
255
-
recordURI,
256
-
contentHash: contentHash as Hash,
257
-
lexicon: collection,
258
-
timestamp: Math.floor(Date.now() / 1000),
259
-
});
172
+
// Get CID from the record response
173
+
const recordCID = record.cid;
260
174
261
-
// Create attestation request
262
-
const attestationRequest = {
263
-
schema: this.config.schemaUID as Hash,
264
-
data: {
265
-
recipient: zeroAddress,
266
-
expirationTime: 0n,
267
-
revocable: true,
268
-
refUID: '0x0000000000000000000000000000000000000000000000000000000000000000' as Hash,
269
-
data: encodedData,
270
-
value: 0n,
271
-
},
272
-
};
175
+
// Initialize SchemaEncoder with the schema string
176
+
const schemaEncoder = new SchemaEncoder(SCHEMA_STRING);
177
+
const encodedData = schemaEncoder.encodeData([
178
+
{ name: "recordURI", value: recordURI, type: "string" },
179
+
{ name: "cid", value: recordCID, type: "string" },
180
+
{ name: "contentHash", value: contentHash, type: "bytes32" },
181
+
{ name: "pds", value: pds, type: "string" },
182
+
{ name: "timestamp", value: Math.floor(Date.now() / 1000), type: "uint256" }
183
+
]);
273
184
274
-
// First simulate to get the return value (UID)
275
-
const { result: uid } = await this.publicClient.simulateContract({
276
-
address: this.config.easContractAddress,
277
-
abi: EAS_ABI,
278
-
functionName: 'attest',
279
-
args: [attestationRequest],
280
-
account: this.account,
281
-
});
185
+
const transaction = await this.eas.attest({
186
+
schema: this.config.schemaUID,
187
+
data: {
188
+
recipient: ethers.ZeroAddress,
189
+
expirationTime: NO_EXPIRATION,
190
+
revocable: false,
191
+
data: encodedData,
192
+
},
193
+
});
282
194
283
-
// Then submit the actual transaction
284
-
const hash = await this.walletClient.writeContract({
285
-
address: this.config.easContractAddress,
286
-
abi: EAS_ABI,
287
-
functionName: 'attest',
288
-
args: [attestationRequest],
289
-
});
195
+
const newAttestationUID = await transaction.wait();
290
196
291
-
// Wait for transaction
292
-
await this.publicClient.waitForTransactionReceipt({ hash });
197
+
return {
198
+
attestationUID: newAttestationUID,
199
+
recordURI,
200
+
cid: recordCID,
201
+
contentHash,
202
+
pds,
203
+
lexicon: collection,
204
+
transactionHash: transaction.tx?.hash,
205
+
explorerURL: getExplorerURL(newAttestationUID, this.network),
206
+
};
207
+
}
293
208
294
-
// Use the UID from simulation
295
-
const attestationUID = uid;
296
-
297
-
return {
298
-
attestationUID,
299
-
recordURI,
300
-
contentHash,
301
-
lexicon: collection,
302
-
transactionHash: hash,
303
-
explorerURL: getExplorerURL(attestationUID, this.network),
304
-
};
305
-
}
306
209
307
210
/**
308
211
* Verify an attestation
309
212
*/
310
213
async verifyAttestation(attestationUID: string): Promise<AttestationData> {
311
-
// Read attestation from contract
312
-
const attestation = await this.publicClient.readContract({
313
-
address: this.config.easContractAddress,
314
-
abi: EAS_ABI,
315
-
functionName: 'getAttestation',
316
-
args: [attestationUID as Hash],
317
-
});
214
+
const attestation = await this.eas.getAttestation(attestationUID);
318
215
319
-
// Check if attestation exists
320
-
if (attestation.uid === '0x0000000000000000000000000000000000000000000000000000000000000000') {
216
+
if (!attestation || attestation.uid === '0x0000000000000000000000000000000000000000000000000000000000000000') {
321
217
throw new Error('Attestation not found');
322
218
}
323
219
324
220
// Decode attestation data
325
-
const decodedData = decodeAttestationData(attestation.data as Hash);
221
+
const schemaEncoder = new SchemaEncoder(SCHEMA_STRING);
222
+
const decodedData = schemaEncoder.decodeData(attestation.data);
223
+
224
+
const recordURI = decodedData.find(d => d.name === 'recordURI')?.value.value as string;
225
+
const cid = decodedData.find(d => d.name === 'cid')?.value.value as string;
226
+
const contentHash = decodedData.find(d => d.name === 'contentHash')?.value.value as string;
227
+
const pds = decodedData.find(d => d.name === 'pds')?.value.value as string;
228
+
const timestamp = Number(decodedData.find(d => d.name === 'timestamp')?.value.value);
229
+
230
+
// Parse lexicon from recordURI (since it's not in schema)
231
+
const { collection: lexicon } = parseRecordURI(recordURI);
326
232
327
233
return {
328
234
uid: attestationUID,
329
-
recordURI: decodedData.recordURI,
330
-
contentHash: decodedData.contentHash,
331
-
lexicon: decodedData.lexicon,
332
-
timestamp: decodedData.timestamp,
235
+
recordURI,
236
+
cid,
237
+
contentHash,
238
+
pds,
239
+
lexicon,
240
+
timestamp,
333
241
attester: attestation.attester,
334
242
revoked: attestation.revocationTime > 0n,
335
243
explorerURL: getExplorerURL(attestationUID, this.network),
336
244
};
337
245
}
246
+
338
247
339
248
/**
340
249
* Compare attestation with current record state
···
345
254
currentHash?: string;
346
255
}> {
347
256
try {
348
-
const record = await this.fetchRecord(attestationData.recordURI);
257
+
// fetchRecord now returns { record, pds }, so we need to destructure
258
+
const { record } = await this.fetchRecord(attestationData.recordURI);
349
259
const currentHash = hashContent(record.value);
350
260
351
261
return {
···
364
274
/**
365
275
* Get signer address
366
276
*/
367
-
getAddress(): Address {
368
-
return this.account.address;
277
+
async getAddress(): Promise<string> {
278
+
return this.signer.getAddress();
369
279
}
370
280
}
+4
src/lib/types.ts
+4
src/lib/types.ts
···
9
9
export interface NotarizationResult {
10
10
attestationUID: string;
11
11
recordURI: string;
12
+
cid: string;
12
13
contentHash: string;
14
+
pds: string;
13
15
lexicon: string;
14
16
transactionHash: string;
15
17
explorerURL: string;
···
18
20
export interface AttestationData {
19
21
uid: string;
20
22
recordURI: string;
23
+
cid: string;
21
24
contentHash: string;
25
+
pds: string;
22
26
lexicon: string;
23
27
timestamp: number;
24
28
attester: string;