@agirails/sdk 2.0.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +183 -0
- package/dist/ACTPClient.d.ts +52 -0
- package/dist/ACTPClient.d.ts.map +1 -0
- package/dist/ACTPClient.js +120 -0
- package/dist/ACTPClient.js.map +1 -0
- package/dist/abi/ACTPKernel.json +1340 -0
- package/dist/abi/ERC20.json +38 -0
- package/dist/abi/EscrowVault.json +64 -0
- package/dist/builders/DeliveryProofBuilder.d.ts +37 -0
- package/dist/builders/DeliveryProofBuilder.d.ts.map +1 -0
- package/dist/builders/DeliveryProofBuilder.js +165 -0
- package/dist/builders/DeliveryProofBuilder.js.map +1 -0
- package/dist/builders/QuoteBuilder.d.ts +68 -0
- package/dist/builders/QuoteBuilder.d.ts.map +1 -0
- package/dist/builders/QuoteBuilder.js +255 -0
- package/dist/builders/QuoteBuilder.js.map +1 -0
- package/dist/builders/index.d.ts +3 -0
- package/dist/builders/index.d.ts.map +1 -0
- package/dist/builders/index.js +10 -0
- package/dist/builders/index.js.map +1 -0
- package/dist/config/networks.d.ts +27 -0
- package/dist/config/networks.d.ts.map +1 -0
- package/dist/config/networks.js +103 -0
- package/dist/config/networks.js.map +1 -0
- package/dist/errors/index.d.ts +38 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +87 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/ACTPKernel.d.ts +30 -0
- package/dist/protocol/ACTPKernel.d.ts.map +1 -0
- package/dist/protocol/ACTPKernel.js +261 -0
- package/dist/protocol/ACTPKernel.js.map +1 -0
- package/dist/protocol/EASHelper.d.ts +23 -0
- package/dist/protocol/EASHelper.d.ts.map +1 -0
- package/dist/protocol/EASHelper.js +106 -0
- package/dist/protocol/EASHelper.js.map +1 -0
- package/dist/protocol/EscrowVault.d.ts +24 -0
- package/dist/protocol/EscrowVault.d.ts.map +1 -0
- package/dist/protocol/EscrowVault.js +114 -0
- package/dist/protocol/EscrowVault.js.map +1 -0
- package/dist/protocol/EventMonitor.d.ts +18 -0
- package/dist/protocol/EventMonitor.d.ts.map +1 -0
- package/dist/protocol/EventMonitor.js +92 -0
- package/dist/protocol/EventMonitor.js.map +1 -0
- package/dist/protocol/MessageSigner.d.ts +23 -0
- package/dist/protocol/MessageSigner.d.ts.map +1 -0
- package/dist/protocol/MessageSigner.js +178 -0
- package/dist/protocol/MessageSigner.js.map +1 -0
- package/dist/protocol/ProofGenerator.d.ts +22 -0
- package/dist/protocol/ProofGenerator.d.ts.map +1 -0
- package/dist/protocol/ProofGenerator.js +64 -0
- package/dist/protocol/ProofGenerator.js.map +1 -0
- package/dist/protocol/QuoteBuilder.d.ts +2 -0
- package/dist/protocol/QuoteBuilder.d.ts.map +1 -0
- package/dist/protocol/QuoteBuilder.js +7 -0
- package/dist/protocol/QuoteBuilder.js.map +1 -0
- package/dist/types/eip712.d.ts +106 -0
- package/dist/types/eip712.d.ts.map +1 -0
- package/dist/types/eip712.js +84 -0
- package/dist/types/eip712.js.map +1 -0
- package/dist/types/escrow.d.ts +18 -0
- package/dist/types/escrow.d.ts.map +1 -0
- package/dist/types/escrow.js +3 -0
- package/dist/types/escrow.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +22 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/message.d.ts +109 -0
- package/dist/types/message.d.ts.map +1 -0
- package/dist/types/message.js +3 -0
- package/dist/types/message.js.map +1 -0
- package/dist/types/state.d.ts +19 -0
- package/dist/types/state.d.ts.map +1 -0
- package/dist/types/state.js +49 -0
- package/dist/types/state.js.map +1 -0
- package/dist/types/transaction.d.ts +36 -0
- package/dist/types/transaction.d.ts.map +1 -0
- package/dist/types/transaction.js +3 -0
- package/dist/types/transaction.js.map +1 -0
- package/dist/utils/IPFSClient.d.ts +37 -0
- package/dist/utils/IPFSClient.d.ts.map +1 -0
- package/dist/utils/IPFSClient.js +128 -0
- package/dist/utils/IPFSClient.js.map +1 -0
- package/dist/utils/NonceManager.d.ts +34 -0
- package/dist/utils/NonceManager.d.ts.map +1 -0
- package/dist/utils/NonceManager.js +114 -0
- package/dist/utils/NonceManager.js.map +1 -0
- package/dist/utils/ReceivedNonceTracker.d.ts +35 -0
- package/dist/utils/ReceivedNonceTracker.d.ts.map +1 -0
- package/dist/utils/ReceivedNonceTracker.js +196 -0
- package/dist/utils/ReceivedNonceTracker.js.map +1 -0
- package/dist/utils/canonicalJson.d.ts +4 -0
- package/dist/utils/canonicalJson.d.ts.map +1 -0
- package/dist/utils/canonicalJson.js +21 -0
- package/dist/utils/canonicalJson.js.map +1 -0
- package/dist/utils/computeTypeHash.d.ts +3 -0
- package/dist/utils/computeTypeHash.d.ts.map +1 -0
- package/dist/utils/computeTypeHash.js +30 -0
- package/dist/utils/computeTypeHash.js.map +1 -0
- package/dist/utils/validation.d.ts +6 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +46 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +73 -0
- package/src/ACTPClient.ts +276 -0
- package/src/__tests__/ProofGenerator.test.ts +124 -0
- package/src/__tests__/QuoteBuilder.test.ts +516 -0
- package/src/__tests__/StateMachine.test.ts +82 -0
- package/src/__tests__/builders/DeliveryProofBuilder.test.ts +581 -0
- package/src/__tests__/integration/ACTPClient.test.ts +263 -0
- package/src/__tests__/integration.test.ts +289 -0
- package/src/__tests__/protocol/EASHelper.test.ts +472 -0
- package/src/__tests__/protocol/EventMonitor.test.ts +382 -0
- package/src/__tests__/security/ACTPKernel.security.test.ts +1167 -0
- package/src/__tests__/security/EscrowVault.security.test.ts +570 -0
- package/src/__tests__/security/MessageSigner.security.test.ts +286 -0
- package/src/__tests__/security/NonceReplay.security.test.ts +501 -0
- package/src/__tests__/security/validation.security.test.ts +376 -0
- package/src/__tests__/utils/IPFSClient.test.ts +262 -0
- package/src/__tests__/utils/NonceManager.test.ts +205 -0
- package/src/__tests__/utils/canonicalJson.test.ts +153 -0
- package/src/abi/ACTPKernel.json +1340 -0
- package/src/abi/ERC20.json +40 -0
- package/src/abi/EscrowVault.json +66 -0
- package/src/builders/DeliveryProofBuilder.ts +326 -0
- package/src/builders/QuoteBuilder.ts +483 -0
- package/src/builders/index.ts +17 -0
- package/src/config/networks.ts +165 -0
- package/src/errors/index.ts +130 -0
- package/src/index.ts +108 -0
- package/src/protocol/ACTPKernel.ts +625 -0
- package/src/protocol/EASHelper.ts +197 -0
- package/src/protocol/EscrowVault.ts +237 -0
- package/src/protocol/EventMonitor.ts +161 -0
- package/src/protocol/MessageSigner.ts +336 -0
- package/src/protocol/ProofGenerator.ts +119 -0
- package/src/protocol/QuoteBuilder.ts +15 -0
- package/src/types/eip712.ts +175 -0
- package/src/types/escrow.ts +26 -0
- package/src/types/index.ts +10 -0
- package/src/types/message.ts +145 -0
- package/src/types/state.ts +77 -0
- package/src/types/transaction.ts +54 -0
- package/src/utils/IPFSClient.ts +248 -0
- package/src/utils/NonceManager.ts +293 -0
- package/src/utils/ReceivedNonceTracker.ts +397 -0
- package/src/utils/canonicalJson.ts +38 -0
- package/src/utils/computeTypeHash.ts +50 -0
- package/src/utils/validation.ts +82 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"inputs": [
|
|
4
|
+
{"name": "spender", "type": "address"},
|
|
5
|
+
{"name": "amount", "type": "uint256"}
|
|
6
|
+
],
|
|
7
|
+
"name": "approve",
|
|
8
|
+
"outputs": [{"name": "", "type": "bool"}],
|
|
9
|
+
"stateMutability": "nonpayable",
|
|
10
|
+
"type": "function"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"inputs": [
|
|
14
|
+
{"name": "account", "type": "address"}
|
|
15
|
+
],
|
|
16
|
+
"name": "balanceOf",
|
|
17
|
+
"outputs": [{"name": "", "type": "uint256"}],
|
|
18
|
+
"stateMutability": "view",
|
|
19
|
+
"type": "function"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"inputs": [
|
|
23
|
+
{"name": "owner", "type": "address"},
|
|
24
|
+
{"name": "spender", "type": "address"}
|
|
25
|
+
],
|
|
26
|
+
"name": "allowance",
|
|
27
|
+
"outputs": [{"name": "", "type": "uint256"}],
|
|
28
|
+
"stateMutability": "view",
|
|
29
|
+
"type": "function"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"inputs": [],
|
|
33
|
+
"name": "decimals",
|
|
34
|
+
"outputs": [{"name": "", "type": "uint8"}],
|
|
35
|
+
"stateMutability": "view",
|
|
36
|
+
"type": "function"
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"inputs": [
|
|
4
|
+
{"name": "kernel", "type": "address"},
|
|
5
|
+
{"name": "txId", "type": "bytes32"},
|
|
6
|
+
{"name": "token", "type": "address"},
|
|
7
|
+
{"name": "amount", "type": "uint256"},
|
|
8
|
+
{"name": "beneficiary", "type": "address"}
|
|
9
|
+
],
|
|
10
|
+
"name": "createEscrow",
|
|
11
|
+
"outputs": [{"name": "escrowId", "type": "bytes32"}],
|
|
12
|
+
"stateMutability": "nonpayable",
|
|
13
|
+
"type": "function"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"inputs": [
|
|
17
|
+
{"name": "escrowId", "type": "bytes32"},
|
|
18
|
+
{"name": "recipients", "type": "address[]"},
|
|
19
|
+
{"name": "amounts", "type": "uint256[]"}
|
|
20
|
+
],
|
|
21
|
+
"name": "disburse",
|
|
22
|
+
"outputs": [],
|
|
23
|
+
"stateMutability": "nonpayable",
|
|
24
|
+
"type": "function"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"inputs": [
|
|
28
|
+
{"name": "escrowId", "type": "bytes32"}
|
|
29
|
+
],
|
|
30
|
+
"name": "escrows",
|
|
31
|
+
"outputs": [
|
|
32
|
+
{"name": "kernel", "type": "address"},
|
|
33
|
+
{"name": "txId", "type": "bytes32"},
|
|
34
|
+
{"name": "token", "type": "address"},
|
|
35
|
+
{"name": "amount", "type": "uint256"},
|
|
36
|
+
{"name": "beneficiary", "type": "address"},
|
|
37
|
+
{"name": "released", "type": "bool"}
|
|
38
|
+
],
|
|
39
|
+
"stateMutability": "view",
|
|
40
|
+
"type": "function"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"anonymous": false,
|
|
44
|
+
"inputs": [
|
|
45
|
+
{"indexed": true, "name": "escrowId", "type": "bytes32"},
|
|
46
|
+
{"indexed": true, "name": "kernel", "type": "address"},
|
|
47
|
+
{"indexed": true, "name": "txId", "type": "bytes32"},
|
|
48
|
+
{"indexed": false, "name": "token", "type": "address"},
|
|
49
|
+
{"indexed": false, "name": "amount", "type": "uint256"}
|
|
50
|
+
],
|
|
51
|
+
"name": "EscrowCreated",
|
|
52
|
+
"type": "event"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"anonymous": false,
|
|
56
|
+
"inputs": [
|
|
57
|
+
{"indexed": true, "name": "escrowId", "type": "bytes32"},
|
|
58
|
+
{"indexed": false, "name": "recipients", "type": "address[]"},
|
|
59
|
+
{"indexed": false, "name": "amounts", "type": "uint256[]"}
|
|
60
|
+
],
|
|
61
|
+
"name": "EscrowReleased",
|
|
62
|
+
"type": "event"
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeliveryProofBuilder - AIP-4 Delivery Proof Construction
|
|
3
|
+
* Reference: AIP-4 §9.1
|
|
4
|
+
*
|
|
5
|
+
* Builds complete delivery proofs with:
|
|
6
|
+
* - IPFS upload of result data
|
|
7
|
+
* - EAS attestation creation
|
|
8
|
+
* - EIP-712 signature
|
|
9
|
+
* - Canonical JSON hashing
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { Signer, verifyTypedData } from 'ethers';
|
|
13
|
+
import { EAS, SchemaEncoder } from '@ethereum-attestation-service/eas-sdk';
|
|
14
|
+
import { computeResultHash } from '../utils/canonicalJson';
|
|
15
|
+
import { IPFSClient } from '../utils/IPFSClient';
|
|
16
|
+
import { NonceManager } from '../utils/NonceManager';
|
|
17
|
+
import { DeliveryProofMessage } from '../types/message';
|
|
18
|
+
import { AIP4DeliveryProofTypes, AIP4DeliveryProofData, EIP712Domain } from '../types/eip712';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* AGIRAILS Delivery Schema UID (Base Sepolia)
|
|
22
|
+
* TODO: Update after schema deployment
|
|
23
|
+
*/
|
|
24
|
+
export const AGIRAILS_DELIVERY_SCHEMA_UID = '0x0000000000000000000000000000000000000000000000000000000000000000'; // PENDING
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Delivery proof build parameters
|
|
28
|
+
*/
|
|
29
|
+
export interface DeliveryProofParams {
|
|
30
|
+
txId: string; // bytes32 (0x-prefixed)
|
|
31
|
+
provider: string; // DID (e.g., "did:ethr:84532:0x...")
|
|
32
|
+
consumer: string; // DID
|
|
33
|
+
resultData: any; // Service result data (will be uploaded to IPFS)
|
|
34
|
+
metadata?: {
|
|
35
|
+
executionTime?: number;
|
|
36
|
+
outputFormat?: string;
|
|
37
|
+
outputSize?: number;
|
|
38
|
+
notes?: string;
|
|
39
|
+
};
|
|
40
|
+
chainId: number; // 84532 (Base Sepolia) or 8453 (Base Mainnet)
|
|
41
|
+
kernelAddress: string; // ACTPKernel contract address
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// IPFSClient and NonceManager interfaces imported from utils
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* DeliveryProofBuilder - Main Builder Class
|
|
48
|
+
*/
|
|
49
|
+
export class DeliveryProofBuilder {
|
|
50
|
+
constructor(
|
|
51
|
+
private ipfs: IPFSClient,
|
|
52
|
+
private signer: Signer,
|
|
53
|
+
private nonceManager: NonceManager,
|
|
54
|
+
private eas: EAS
|
|
55
|
+
) {}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Build complete delivery proof
|
|
59
|
+
* Reference: AIP-4 §5.1 (Provider workflow steps 1-9)
|
|
60
|
+
*
|
|
61
|
+
* @param params - Delivery proof parameters
|
|
62
|
+
* @returns Delivery proof message, IPFS CID, and attestation UID
|
|
63
|
+
*/
|
|
64
|
+
async build(params: DeliveryProofParams): Promise<{
|
|
65
|
+
deliveryProof: DeliveryProofMessage;
|
|
66
|
+
deliveryProofCID: string;
|
|
67
|
+
attestationUID: string;
|
|
68
|
+
}> {
|
|
69
|
+
// Step 1: Upload result to IPFS
|
|
70
|
+
const resultCID = await this.ipfs.add(JSON.stringify(params.resultData));
|
|
71
|
+
await this.ipfs.pin(resultCID); // Permanent pinning
|
|
72
|
+
|
|
73
|
+
// Step 2: Compute result hash (canonical JSON)
|
|
74
|
+
const resultHash = computeResultHash(params.resultData);
|
|
75
|
+
|
|
76
|
+
// Step 3: Create EAS attestation on-chain
|
|
77
|
+
const deliveredAt = Math.floor(Date.now() / 1000);
|
|
78
|
+
|
|
79
|
+
const schemaEncoder = new SchemaEncoder(
|
|
80
|
+
'bytes32 txId,string resultCID,bytes32 resultHash,uint256 deliveredAt'
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Extract Ethereum address from consumer DID
|
|
84
|
+
const consumerAddress = this.extractAddressFromDID(params.consumer);
|
|
85
|
+
|
|
86
|
+
const encodedData = schemaEncoder.encodeData([
|
|
87
|
+
{ name: 'txId', value: params.txId, type: 'bytes32' },
|
|
88
|
+
{ name: 'resultCID', value: resultCID, type: 'string' },
|
|
89
|
+
{ name: 'resultHash', value: resultHash, type: 'bytes32' },
|
|
90
|
+
{ name: 'deliveredAt', value: deliveredAt, type: 'uint256' }
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
const tx = await this.eas.attest({
|
|
94
|
+
schema: AGIRAILS_DELIVERY_SCHEMA_UID,
|
|
95
|
+
data: {
|
|
96
|
+
recipient: consumerAddress,
|
|
97
|
+
expirationTime: 0n, // No expiration (use bigint)
|
|
98
|
+
revocable: false,
|
|
99
|
+
data: encodedData
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const receipt = await tx.wait();
|
|
104
|
+
// EAS SDK returns attestation UID directly in newAttestationUID
|
|
105
|
+
const attestationUID = (receipt as any).newAttestationUID || (receipt as any);
|
|
106
|
+
|
|
107
|
+
// Step 4: Build delivery proof message (unsigned)
|
|
108
|
+
const deliveryProof: DeliveryProofMessage = {
|
|
109
|
+
type: 'agirails.delivery.v1',
|
|
110
|
+
version: '1.0.0',
|
|
111
|
+
txId: params.txId,
|
|
112
|
+
provider: params.provider,
|
|
113
|
+
consumer: params.consumer,
|
|
114
|
+
resultCID,
|
|
115
|
+
resultHash,
|
|
116
|
+
metadata: params.metadata || {},
|
|
117
|
+
easAttestationUID: attestationUID,
|
|
118
|
+
deliveredAt,
|
|
119
|
+
chainId: params.chainId,
|
|
120
|
+
nonce: this.nonceManager.getNextNonce('agirails.delivery.v1'),
|
|
121
|
+
signature: '' // Filled in next step
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Step 5: Sign with EIP-712
|
|
125
|
+
const signature = await this.signDeliveryProof(deliveryProof, params.kernelAddress);
|
|
126
|
+
deliveryProof.signature = signature;
|
|
127
|
+
|
|
128
|
+
// Step 6: Upload delivery proof to IPFS
|
|
129
|
+
const deliveryProofCID = await this.ipfs.add(JSON.stringify(deliveryProof));
|
|
130
|
+
await this.ipfs.pin(deliveryProofCID); // Permanent
|
|
131
|
+
|
|
132
|
+
// Record nonce usage
|
|
133
|
+
this.nonceManager.recordNonce('agirails.delivery.v1', deliveryProof.nonce);
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
deliveryProof,
|
|
137
|
+
deliveryProofCID,
|
|
138
|
+
attestationUID
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Verify delivery proof signature and integrity
|
|
144
|
+
* Reference: AIP-4 §5.2 (Consumer verification)
|
|
145
|
+
*
|
|
146
|
+
* @param deliveryProof - Delivery proof message
|
|
147
|
+
* @param resultData - Actual result data (from IPFS)
|
|
148
|
+
* @param kernelAddress - ACTPKernel contract address
|
|
149
|
+
* @returns true if valid, throws error otherwise
|
|
150
|
+
*/
|
|
151
|
+
async verify(
|
|
152
|
+
deliveryProof: DeliveryProofMessage,
|
|
153
|
+
resultData: any,
|
|
154
|
+
kernelAddress: string
|
|
155
|
+
): Promise<boolean> {
|
|
156
|
+
// 1. Verify signature
|
|
157
|
+
const recoveredAddress = await this.recoverDeliveryProofSigner(deliveryProof, kernelAddress);
|
|
158
|
+
const expectedAddress = this.extractAddressFromDID(deliveryProof.provider);
|
|
159
|
+
|
|
160
|
+
if (recoveredAddress.toLowerCase() !== expectedAddress.toLowerCase()) {
|
|
161
|
+
throw new Error('Invalid signature: recovered address does not match provider');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 2. Verify result hash
|
|
165
|
+
const computedHash = computeResultHash(resultData);
|
|
166
|
+
|
|
167
|
+
if (computedHash !== deliveryProof.resultHash) {
|
|
168
|
+
throw new Error('Result hash mismatch - data may be tampered');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// 3. Verify EAS attestation exists and is valid
|
|
172
|
+
const attestation = await this.eas.getAttestation(deliveryProof.easAttestationUID);
|
|
173
|
+
|
|
174
|
+
if (!attestation) {
|
|
175
|
+
throw new Error('Attestation not found on EAS');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (attestation.schema !== AGIRAILS_DELIVERY_SCHEMA_UID) {
|
|
179
|
+
throw new Error(`Invalid attestation schema: expected ${AGIRAILS_DELIVERY_SCHEMA_UID}, got ${attestation.schema}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Check if attestation is revoked (property may not exist in all EAS SDK versions)
|
|
183
|
+
if ('revoked' in attestation && (attestation as any).revoked) {
|
|
184
|
+
throw new Error('Attestation was revoked');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if ('revocationTime' in attestation && (attestation as any).revocationTime > 0) {
|
|
188
|
+
throw new Error('Attestation was revoked');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// 4. Verify attestation data matches delivery proof
|
|
192
|
+
const schemaEncoder = new SchemaEncoder(
|
|
193
|
+
'bytes32 txId,string resultCID,bytes32 resultHash,uint256 deliveredAt'
|
|
194
|
+
);
|
|
195
|
+
const decodedData = schemaEncoder.decodeData(attestation.data);
|
|
196
|
+
|
|
197
|
+
const attestationTxId = decodedData.find((d) => d.name === 'txId')?.value.value;
|
|
198
|
+
const attestationResultCID = decodedData.find((d) => d.name === 'resultCID')?.value.value;
|
|
199
|
+
const attestationResultHash = decodedData.find((d) => d.name === 'resultHash')?.value.value;
|
|
200
|
+
|
|
201
|
+
if (attestationTxId !== deliveryProof.txId) {
|
|
202
|
+
throw new Error('Attestation txId mismatch');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (attestationResultCID !== deliveryProof.resultCID) {
|
|
206
|
+
throw new Error('Attestation resultCID mismatch');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (attestationResultHash !== deliveryProof.resultHash) {
|
|
210
|
+
throw new Error('Attestation resultHash mismatch');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Sign delivery proof with EIP-712
|
|
218
|
+
* Reference: AIP-4 §3.3
|
|
219
|
+
*
|
|
220
|
+
* @param deliveryProof - Delivery proof message (unsigned)
|
|
221
|
+
* @param kernelAddress - ACTPKernel contract address
|
|
222
|
+
* @returns EIP-712 signature (0x-prefixed, 130 chars)
|
|
223
|
+
*/
|
|
224
|
+
private async signDeliveryProof(
|
|
225
|
+
deliveryProof: DeliveryProofMessage,
|
|
226
|
+
kernelAddress: string
|
|
227
|
+
): Promise<string> {
|
|
228
|
+
const domain: EIP712Domain = {
|
|
229
|
+
name: 'AGIRAILS',
|
|
230
|
+
version: '1',
|
|
231
|
+
chainId: deliveryProof.chainId,
|
|
232
|
+
verifyingContract: kernelAddress
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const message: AIP4DeliveryProofData = {
|
|
236
|
+
txId: deliveryProof.txId,
|
|
237
|
+
provider: deliveryProof.provider,
|
|
238
|
+
consumer: deliveryProof.consumer,
|
|
239
|
+
resultCID: deliveryProof.resultCID,
|
|
240
|
+
resultHash: deliveryProof.resultHash,
|
|
241
|
+
easAttestationUID: deliveryProof.easAttestationUID,
|
|
242
|
+
deliveredAt: deliveryProof.deliveredAt,
|
|
243
|
+
chainId: deliveryProof.chainId,
|
|
244
|
+
nonce: deliveryProof.nonce
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// Sign using ethers v6 signTypedData API
|
|
248
|
+
// ethers v6 uses signTypedData() (without underscore)
|
|
249
|
+
if ('signTypedData' in this.signer && typeof (this.signer as any).signTypedData === 'function') {
|
|
250
|
+
const signature = await (this.signer as any).signTypedData(
|
|
251
|
+
domain,
|
|
252
|
+
AIP4DeliveryProofTypes,
|
|
253
|
+
message
|
|
254
|
+
);
|
|
255
|
+
return signature;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
throw new Error('Signer does not support EIP-712 typed data signing (ethers v6+)');
|
|
259
|
+
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Recover signer address from delivery proof signature
|
|
264
|
+
*
|
|
265
|
+
* @param deliveryProof - Delivery proof message
|
|
266
|
+
* @param kernelAddress - ACTPKernel contract address
|
|
267
|
+
* @returns Recovered Ethereum address
|
|
268
|
+
*/
|
|
269
|
+
private async recoverDeliveryProofSigner(
|
|
270
|
+
deliveryProof: DeliveryProofMessage,
|
|
271
|
+
kernelAddress: string
|
|
272
|
+
): Promise<string> {
|
|
273
|
+
const domain: EIP712Domain = {
|
|
274
|
+
name: 'AGIRAILS',
|
|
275
|
+
version: '1',
|
|
276
|
+
chainId: deliveryProof.chainId,
|
|
277
|
+
verifyingContract: kernelAddress
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const message: AIP4DeliveryProofData = {
|
|
281
|
+
txId: deliveryProof.txId,
|
|
282
|
+
provider: deliveryProof.provider,
|
|
283
|
+
consumer: deliveryProof.consumer,
|
|
284
|
+
resultCID: deliveryProof.resultCID,
|
|
285
|
+
resultHash: deliveryProof.resultHash,
|
|
286
|
+
easAttestationUID: deliveryProof.easAttestationUID,
|
|
287
|
+
deliveredAt: deliveryProof.deliveredAt,
|
|
288
|
+
chainId: deliveryProof.chainId,
|
|
289
|
+
nonce: deliveryProof.nonce
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// Recover address using ethers.js verifyTypedData
|
|
293
|
+
// Wrap in try-catch to handle low-level cryptographic errors gracefully
|
|
294
|
+
try {
|
|
295
|
+
const recoveredAddress = verifyTypedData(
|
|
296
|
+
domain,
|
|
297
|
+
AIP4DeliveryProofTypes,
|
|
298
|
+
message,
|
|
299
|
+
deliveryProof.signature
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
return recoveredAddress;
|
|
303
|
+
} catch (error: any) {
|
|
304
|
+
// Wrap low-level cryptographic errors in meaningful error message
|
|
305
|
+
throw new Error(`Invalid signature: ${error.message || 'signature verification failed'}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Extract Ethereum address from DID
|
|
311
|
+
* Supports: did:ethr:0x... and did:ethr:84532:0x...
|
|
312
|
+
*
|
|
313
|
+
* @param did - DID string
|
|
314
|
+
* @returns Ethereum address (0x-prefixed, checksummed)
|
|
315
|
+
*/
|
|
316
|
+
private extractAddressFromDID(did: string): string {
|
|
317
|
+
const parts = did.replace('did:ethr:', '').split(':');
|
|
318
|
+
const address = parts.length === 2 ? parts[1] : parts[0];
|
|
319
|
+
|
|
320
|
+
if (!address.startsWith('0x') || address.length !== 42) {
|
|
321
|
+
throw new Error(`Invalid DID format: ${did}`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return address;
|
|
325
|
+
}
|
|
326
|
+
}
|