easctl 0.1.2 → 0.2.0
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 +1 -1
- package/dist/index.js +206 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/commands/attest.test.ts +1 -0
- package/src/__tests__/commands/multi-attest.test.ts +4 -0
- package/src/__tests__/commands/multi-revoke.test.ts +4 -0
- package/src/__tests__/commands/offchain-attest.test.ts +1 -0
- package/src/__tests__/commands/query-attestations.test.ts +1 -0
- package/src/__tests__/commands/query-schema.test.ts +1 -0
- package/src/__tests__/commands/revoke.test.ts +1 -0
- package/src/__tests__/commands/schema-get.test.ts +1 -0
- package/src/__tests__/commands/status.test.ts +121 -0
- package/src/__tests__/config.test.ts +4 -4
- package/src/commands/attest.ts +3 -3
- package/src/commands/clear-key.ts +1 -1
- package/src/commands/multi-attest.ts +2 -0
- package/src/commands/multi-revoke.ts +2 -0
- package/src/commands/offchain-attest.ts +3 -3
- package/src/commands/popular-schemas.ts +43 -0
- package/src/commands/query-attestations.ts +3 -3
- package/src/commands/query-schema.ts +3 -3
- package/src/commands/revoke.ts +3 -3
- package/src/commands/schema-get.ts +3 -3
- package/src/commands/set-key.ts +1 -1
- package/src/commands/status.ts +49 -0
- package/src/config.ts +1 -1
- package/src/index.ts +6 -0
- package/src/popular-schemas.ts +119 -0
- package/src/validation.ts +8 -0
package/package.json
CHANGED
|
@@ -25,6 +25,7 @@ vi.mock('../../stdin.js', () => ({
|
|
|
25
25
|
vi.mock('../../validation.js', () => ({
|
|
26
26
|
validateAddress: vi.fn(),
|
|
27
27
|
validateBytes32: vi.fn(),
|
|
28
|
+
resolveAndValidateSchemaUID: vi.fn((v: string) => v),
|
|
28
29
|
}));
|
|
29
30
|
|
|
30
31
|
const mockEncodeData = vi.fn().mockReturnValue('0xencoded');
|
|
@@ -22,6 +22,10 @@ vi.mock('../../stdin.js', () => ({
|
|
|
22
22
|
resolveInput: vi.fn((v: string) => Promise.resolve(v)),
|
|
23
23
|
}));
|
|
24
24
|
|
|
25
|
+
vi.mock('../../popular-schemas.js', () => ({
|
|
26
|
+
resolveSchemaUID: vi.fn((v: string) => v),
|
|
27
|
+
}));
|
|
28
|
+
|
|
25
29
|
const mockEncodeData = vi.fn().mockReturnValue('0xencoded');
|
|
26
30
|
|
|
27
31
|
vi.mock('@ethereum-attestation-service/eas-sdk', () => ({
|
|
@@ -21,6 +21,10 @@ vi.mock('../../stdin.js', () => ({
|
|
|
21
21
|
resolveInput: vi.fn((v: string) => Promise.resolve(v)),
|
|
22
22
|
}));
|
|
23
23
|
|
|
24
|
+
vi.mock('../../popular-schemas.js', () => ({
|
|
25
|
+
resolveSchemaUID: vi.fn((v: string) => v),
|
|
26
|
+
}));
|
|
27
|
+
|
|
24
28
|
import { multiRevokeCommand } from '../../commands/multi-revoke.js';
|
|
25
29
|
import { output, handleError } from '../../output.js';
|
|
26
30
|
|
|
@@ -16,6 +16,7 @@ vi.mock('../../output.js', () => ({
|
|
|
16
16
|
vi.mock('../../validation.js', () => ({
|
|
17
17
|
validateAddress: vi.fn(),
|
|
18
18
|
validateBytes32: vi.fn(),
|
|
19
|
+
resolveAndValidateSchemaUID: vi.fn((v: string) => v),
|
|
19
20
|
}));
|
|
20
21
|
|
|
21
22
|
import { queryAttestationsCommand } from '../../commands/query-attestations.js';
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
vi.mock('../../output.js', () => ({
|
|
4
|
+
output: vi.fn(),
|
|
5
|
+
handleError: vi.fn(),
|
|
6
|
+
}));
|
|
7
|
+
|
|
8
|
+
vi.mock('../../config.js', () => ({
|
|
9
|
+
getStoredPrivateKey: vi.fn(() => undefined),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
import { statusCommand } from '../../commands/status.js';
|
|
13
|
+
import { output } from '../../output.js';
|
|
14
|
+
import { getStoredPrivateKey } from '../../config.js';
|
|
15
|
+
|
|
16
|
+
describe('status command', () => {
|
|
17
|
+
const originalEnv = process.env.EAS_PRIVATE_KEY;
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
vi.clearAllMocks();
|
|
21
|
+
delete process.env.EAS_PRIVATE_KEY;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
if (originalEnv !== undefined) {
|
|
26
|
+
process.env.EAS_PRIVATE_KEY = originalEnv;
|
|
27
|
+
} else {
|
|
28
|
+
delete process.env.EAS_PRIVATE_KEY;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
async function runCommand(args: string[] = []) {
|
|
33
|
+
await statusCommand.parseAsync(['node', 'test', ...args]);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
it('shows key not set when no key is configured', async () => {
|
|
37
|
+
await runCommand();
|
|
38
|
+
|
|
39
|
+
expect(output).toHaveBeenCalledWith({
|
|
40
|
+
success: true,
|
|
41
|
+
data: expect.objectContaining({
|
|
42
|
+
wallet: { privateKey: 'not set', address: 'n/a' },
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('shows address from env var with source', async () => {
|
|
48
|
+
process.env.EAS_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
|
|
49
|
+
await runCommand();
|
|
50
|
+
|
|
51
|
+
expect(output).toHaveBeenCalledWith({
|
|
52
|
+
success: true,
|
|
53
|
+
data: expect.objectContaining({
|
|
54
|
+
wallet: {
|
|
55
|
+
privateKey: 'set (env)',
|
|
56
|
+
address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
|
|
57
|
+
},
|
|
58
|
+
}),
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('shows address from stored key', async () => {
|
|
63
|
+
vi.mocked(getStoredPrivateKey).mockReturnValue('0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80');
|
|
64
|
+
await runCommand();
|
|
65
|
+
|
|
66
|
+
const call = (output as any).mock.calls[0][0];
|
|
67
|
+
expect(call.data.wallet.privateKey).toBe('set (stored)');
|
|
68
|
+
expect(call.data.wallet.address).toBe('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('shows address when key has no 0x prefix', async () => {
|
|
72
|
+
process.env.EAS_PRIVATE_KEY = 'ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
|
|
73
|
+
await runCommand();
|
|
74
|
+
|
|
75
|
+
const call = (output as any).mock.calls[0][0];
|
|
76
|
+
expect(call.data.wallet.privateKey).toBe('set (env)');
|
|
77
|
+
expect(call.data.wallet.address).toBe('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('shows invalid format for a bad key', async () => {
|
|
81
|
+
process.env.EAS_PRIVATE_KEY = 'not-a-real-key';
|
|
82
|
+
await runCommand();
|
|
83
|
+
|
|
84
|
+
const call = (output as any).mock.calls[0][0];
|
|
85
|
+
expect(call.data.wallet.privateKey).toBe('set (invalid format)');
|
|
86
|
+
expect(call.data.wallet.address).toBe('n/a');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('defaults to ethereum chain', async () => {
|
|
90
|
+
await runCommand();
|
|
91
|
+
|
|
92
|
+
const call = (output as any).mock.calls[0][0];
|
|
93
|
+
expect(call.data.chain.name).toBe('ethereum');
|
|
94
|
+
expect(call.data.chain.chainId).toBe(1);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('shows correct config for --chain base', async () => {
|
|
98
|
+
await runCommand(['--chain', 'base']);
|
|
99
|
+
|
|
100
|
+
const call = (output as any).mock.calls[0][0];
|
|
101
|
+
expect(call.data.chain.name).toBe('base');
|
|
102
|
+
expect(call.data.chain.chainId).toBe(8453);
|
|
103
|
+
expect(call.data.contracts.eas).toBe('0x4200000000000000000000000000000000000021');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('uses custom rpc-url when provided', async () => {
|
|
107
|
+
await runCommand(['--rpc-url', 'https://custom.rpc']);
|
|
108
|
+
|
|
109
|
+
const call = (output as any).mock.calls[0][0];
|
|
110
|
+
expect(call.data.chain.rpcUrl).toBe('https://custom.rpc');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('lists supported chains', async () => {
|
|
114
|
+
await runCommand();
|
|
115
|
+
|
|
116
|
+
const call = (output as any).mock.calls[0][0];
|
|
117
|
+
expect(call.data.supportedChains).toContain('ethereum');
|
|
118
|
+
expect(call.data.supportedChains).toContain('base');
|
|
119
|
+
expect(call.data.supportedChains).toContain('sepolia');
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -22,8 +22,8 @@ describe('config module', () => {
|
|
|
22
22
|
beforeEach(() => vi.clearAllMocks());
|
|
23
23
|
|
|
24
24
|
describe('getConfigPath', () => {
|
|
25
|
-
it('returns ~/.
|
|
26
|
-
expect(getConfigPath()).toBe('/mock/home/.
|
|
25
|
+
it('returns ~/.easctl', () => {
|
|
26
|
+
expect(getConfigPath()).toBe('/mock/home/.easctl');
|
|
27
27
|
});
|
|
28
28
|
});
|
|
29
29
|
|
|
@@ -61,7 +61,7 @@ describe('config module', () => {
|
|
|
61
61
|
vi.mocked(readFileSync).mockReturnValue('{}');
|
|
62
62
|
setStoredPrivateKey('0xabc123');
|
|
63
63
|
expect(writeFileSync).toHaveBeenCalledWith(
|
|
64
|
-
'/mock/home/.
|
|
64
|
+
'/mock/home/.easctl',
|
|
65
65
|
JSON.stringify({ privateKey: '0xabc123' }, null, 2) + '\n',
|
|
66
66
|
{ mode: 0o600 },
|
|
67
67
|
);
|
|
@@ -71,7 +71,7 @@ describe('config module', () => {
|
|
|
71
71
|
vi.mocked(readFileSync).mockReturnValue('{}');
|
|
72
72
|
setStoredPrivateKey('abc123');
|
|
73
73
|
expect(writeFileSync).toHaveBeenCalledWith(
|
|
74
|
-
'/mock/home/.
|
|
74
|
+
'/mock/home/.easctl',
|
|
75
75
|
JSON.stringify({ privateKey: '0xabc123' }, null, 2) + '\n',
|
|
76
76
|
{ mode: 0o600 },
|
|
77
77
|
);
|
package/src/commands/attest.ts
CHANGED
|
@@ -3,11 +3,11 @@ import { SchemaEncoder, NO_EXPIRATION, ZERO_BYTES32 } from '@ethereum-attestatio
|
|
|
3
3
|
import { createEASClient } from '../client.js';
|
|
4
4
|
import { output, handleError } from '../output.js';
|
|
5
5
|
import { resolveInput } from '../stdin.js';
|
|
6
|
-
import { validateAddress,
|
|
6
|
+
import { validateAddress, resolveAndValidateSchemaUID } from '../validation.js';
|
|
7
7
|
|
|
8
8
|
export const attestCommand = new Command('attest')
|
|
9
9
|
.description('Create an on-chain attestation')
|
|
10
|
-
.requiredOption('-s, --schema <uid>', 'Schema UID
|
|
10
|
+
.requiredOption('-s, --schema <uid>', 'Schema UID or popular schema name')
|
|
11
11
|
.requiredOption('-d, --data <json>', 'Attestation data as JSON array: [{"name":"field","type":"uint256","value":"123"}]')
|
|
12
12
|
.option('-r, --recipient <address>', 'Recipient address', '0x0000000000000000000000000000000000000000')
|
|
13
13
|
.option('--ref-uid <uid>', 'Referenced attestation UID', ZERO_BYTES32)
|
|
@@ -20,7 +20,7 @@ export const attestCommand = new Command('attest')
|
|
|
20
20
|
.option('--dry-run', 'Estimate gas without sending the transaction')
|
|
21
21
|
.action(async (opts) => {
|
|
22
22
|
try {
|
|
23
|
-
|
|
23
|
+
opts.schema = resolveAndValidateSchemaUID(opts.schema, 'schema UID');
|
|
24
24
|
if (opts.recipient !== '0x0000000000000000000000000000000000000000') {
|
|
25
25
|
validateAddress(opts.recipient, 'recipient');
|
|
26
26
|
}
|
|
@@ -3,7 +3,7 @@ import { clearStoredPrivateKey, getStoredPrivateKey } from '../config.js';
|
|
|
3
3
|
import { output } from '../output.js';
|
|
4
4
|
|
|
5
5
|
export const clearKeyCommand = new Command('clear-key')
|
|
6
|
-
.description('Remove the stored private key from ~/.
|
|
6
|
+
.description('Remove the stored private key from ~/.easctl')
|
|
7
7
|
.action(() => {
|
|
8
8
|
if (!getStoredPrivateKey()) {
|
|
9
9
|
output({ success: true, data: { cleared: false, message: 'No private key is currently stored' } });
|
|
@@ -3,6 +3,7 @@ import { SchemaEncoder, NO_EXPIRATION, ZERO_BYTES32 } from '@ethereum-attestatio
|
|
|
3
3
|
import { createEASClient } from '../client.js';
|
|
4
4
|
import { output, handleError } from '../output.js';
|
|
5
5
|
import { resolveInput } from '../stdin.js';
|
|
6
|
+
import { resolveSchemaUID } from '../popular-schemas.js';
|
|
6
7
|
|
|
7
8
|
interface AttestationInput {
|
|
8
9
|
schema: string;
|
|
@@ -34,6 +35,7 @@ export const multiAttestCommand = new Command('multi-attest')
|
|
|
34
35
|
const grouped = new Map<string, { schema: string; data: any[] }>();
|
|
35
36
|
|
|
36
37
|
for (const input of inputs) {
|
|
38
|
+
input.schema = resolveSchemaUID(input.schema);
|
|
37
39
|
const schemaString = input.data.map((item) => `${item.type} ${item.name}`).join(', ');
|
|
38
40
|
const encoder = new SchemaEncoder(schemaString);
|
|
39
41
|
const encodedData = encoder.encodeData(input.data as any);
|
|
@@ -2,6 +2,7 @@ import { Command } from 'commander';
|
|
|
2
2
|
import { createEASClient } from '../client.js';
|
|
3
3
|
import { output, handleError } from '../output.js';
|
|
4
4
|
import { resolveInput } from '../stdin.js';
|
|
5
|
+
import { resolveSchemaUID } from '../popular-schemas.js';
|
|
5
6
|
|
|
6
7
|
interface RevocationInput {
|
|
7
8
|
schema: string;
|
|
@@ -29,6 +30,7 @@ export const multiRevokeCommand = new Command('multi-revoke')
|
|
|
29
30
|
const grouped = new Map<string, { schema: string; data: any[] }>();
|
|
30
31
|
|
|
31
32
|
for (const input of inputs) {
|
|
33
|
+
input.schema = resolveSchemaUID(input.schema);
|
|
32
34
|
if (!grouped.has(input.schema)) {
|
|
33
35
|
grouped.set(input.schema, { schema: input.schema, data: [] });
|
|
34
36
|
}
|
|
@@ -8,12 +8,12 @@ import {
|
|
|
8
8
|
import { createEASClient } from '../client.js';
|
|
9
9
|
import { output, handleError } from '../output.js';
|
|
10
10
|
import { resolveInput } from '../stdin.js';
|
|
11
|
-
import { validateAddress,
|
|
11
|
+
import { validateAddress, resolveAndValidateSchemaUID } from '../validation.js';
|
|
12
12
|
import { EASSCAN_URLS } from '../graphql.js';
|
|
13
13
|
|
|
14
14
|
export const offchainAttestCommand = new Command('offchain-attest')
|
|
15
15
|
.description('Create an off-chain attestation (signed but not submitted on-chain)')
|
|
16
|
-
.requiredOption('-s, --schema <uid>', 'Schema UID')
|
|
16
|
+
.requiredOption('-s, --schema <uid>', 'Schema UID or popular schema name')
|
|
17
17
|
.requiredOption('-d, --data <json>', 'Attestation data as JSON array: [{"name":"field","type":"uint256","value":"123"}]')
|
|
18
18
|
.option('-r, --recipient <address>', 'Recipient address', '0x0000000000000000000000000000000000000000')
|
|
19
19
|
.option('--ref-uid <uid>', 'Referenced attestation UID', ZERO_BYTES32)
|
|
@@ -24,7 +24,7 @@ export const offchainAttestCommand = new Command('offchain-attest')
|
|
|
24
24
|
.option('--rpc-url <url>', 'Custom RPC URL')
|
|
25
25
|
.action(async (opts) => {
|
|
26
26
|
try {
|
|
27
|
-
|
|
27
|
+
opts.schema = resolveAndValidateSchemaUID(opts.schema, 'schema UID');
|
|
28
28
|
if (opts.recipient !== '0x0000000000000000000000000000000000000000') {
|
|
29
29
|
validateAddress(opts.recipient, 'recipient');
|
|
30
30
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { output, isJsonMode, handleError } from '../output.js';
|
|
3
|
+
import { listPopularSchemas, listPopularSchemasByCategory } from '../popular-schemas.js';
|
|
4
|
+
|
|
5
|
+
export const popularSchemasCommand = new Command('popular-schemas')
|
|
6
|
+
.description('List popular EAS schemas with names, UIDs, and descriptions')
|
|
7
|
+
.option('--category <name>', 'Filter by category (general, identity, social)')
|
|
8
|
+
.action(async (opts) => {
|
|
9
|
+
try {
|
|
10
|
+
if (isJsonMode()) {
|
|
11
|
+
const schemas = opts.category
|
|
12
|
+
? listPopularSchemas().filter((s) => s.category === opts.category)
|
|
13
|
+
: listPopularSchemas();
|
|
14
|
+
if (opts.category && schemas.length === 0) {
|
|
15
|
+
const categories = [...new Set(listPopularSchemas().map((s) => s.category))];
|
|
16
|
+
throw new Error(`Unknown category "${opts.category}". Available: ${categories.join(', ')}`);
|
|
17
|
+
}
|
|
18
|
+
output({ success: true, data: { count: schemas.length, schemas } as any });
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const byCategory = listPopularSchemasByCategory();
|
|
23
|
+
const categories = opts.category ? { [opts.category]: byCategory[opts.category] } : byCategory;
|
|
24
|
+
|
|
25
|
+
if (opts.category && !byCategory[opts.category]) {
|
|
26
|
+
const available = Object.keys(byCategory).join(', ');
|
|
27
|
+
throw new Error(`Unknown category "${opts.category}". Available: ${available}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
for (const [category, schemas] of Object.entries(categories)) {
|
|
31
|
+
console.log(`\n=== ${category} ===\n`);
|
|
32
|
+
for (const s of schemas) {
|
|
33
|
+
console.log(` ${s.name}`);
|
|
34
|
+
console.log(` Schema: ${s.schema}`);
|
|
35
|
+
console.log(` UID: ${s.uid}`);
|
|
36
|
+
console.log(` Revocable: ${s.revocable ? 'yes' : 'no'}`);
|
|
37
|
+
console.log(` ${s.description}\n`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
} catch (err) {
|
|
41
|
+
handleError(err);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { graphqlQuery, QUERIES } from '../graphql.js';
|
|
3
3
|
import { output, handleError } from '../output.js';
|
|
4
|
-
import { validateAddress,
|
|
4
|
+
import { validateAddress, resolveAndValidateSchemaUID } from '../validation.js';
|
|
5
5
|
|
|
6
6
|
export const queryAttestationsCommand = new Command('query-attestations')
|
|
7
7
|
.description('Query attestations by schema or attester from the EAS GraphQL API')
|
|
8
|
-
.option('-s, --schema <uid>', 'Filter by schema UID')
|
|
8
|
+
.option('-s, --schema <uid>', 'Filter by schema UID or popular schema name')
|
|
9
9
|
.option('-a, --attester <address>', 'Filter by attester address')
|
|
10
10
|
.option('-n, --limit <number>', 'Max results to return', '10')
|
|
11
11
|
.option('--skip <number>', 'Number of results to skip (for pagination)', '0')
|
|
@@ -15,7 +15,7 @@ export const queryAttestationsCommand = new Command('query-attestations')
|
|
|
15
15
|
if (!opts.schema && !opts.attester) {
|
|
16
16
|
throw new Error('Provide at least one filter: --schema or --attester');
|
|
17
17
|
}
|
|
18
|
-
if (opts.schema)
|
|
18
|
+
if (opts.schema) opts.schema = resolveAndValidateSchemaUID(opts.schema, 'schema UID');
|
|
19
19
|
if (opts.attester) validateAddress(opts.attester, 'attester');
|
|
20
20
|
|
|
21
21
|
const take = parseInt(opts.limit, 10);
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { graphqlQuery, QUERIES } from '../graphql.js';
|
|
3
3
|
import { output, handleError } from '../output.js';
|
|
4
|
-
import {
|
|
4
|
+
import { resolveAndValidateSchemaUID } from '../validation.js';
|
|
5
5
|
|
|
6
6
|
export const querySchemaCommand = new Command('query-schema')
|
|
7
7
|
.description('Query a schema from the EAS GraphQL API')
|
|
8
|
-
.requiredOption('-u, --uid <uid>', 'Schema UID')
|
|
8
|
+
.requiredOption('-u, --uid <uid>', 'Schema UID or popular schema name')
|
|
9
9
|
.option('-c, --chain <name>', 'Chain name', 'ethereum')
|
|
10
10
|
.action(async (opts) => {
|
|
11
11
|
try {
|
|
12
|
-
|
|
12
|
+
opts.uid = resolveAndValidateSchemaUID(opts.uid, 'schema UID');
|
|
13
13
|
|
|
14
14
|
const data = await graphqlQuery(opts.chain, QUERIES.getSchema, { id: opts.uid });
|
|
15
15
|
|
package/src/commands/revoke.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { createEASClient } from '../client.js';
|
|
3
3
|
import { output, handleError } from '../output.js';
|
|
4
|
-
import { validateBytes32 } from '../validation.js';
|
|
4
|
+
import { validateBytes32, resolveAndValidateSchemaUID } from '../validation.js';
|
|
5
5
|
|
|
6
6
|
export const revokeCommand = new Command('revoke')
|
|
7
7
|
.description('Revoke an on-chain attestation')
|
|
8
|
-
.requiredOption('-s, --schema <uid>', 'Schema UID
|
|
8
|
+
.requiredOption('-s, --schema <uid>', 'Schema UID or popular schema name')
|
|
9
9
|
.requiredOption('-u, --uid <uid>', 'Attestation UID to revoke')
|
|
10
10
|
.option('--value <wei>', 'ETH value to send (in wei)', '0')
|
|
11
11
|
.option('-c, --chain <name>', 'Chain name', 'ethereum')
|
|
@@ -13,7 +13,7 @@ export const revokeCommand = new Command('revoke')
|
|
|
13
13
|
.option('--dry-run', 'Estimate gas without sending the transaction')
|
|
14
14
|
.action(async (opts) => {
|
|
15
15
|
try {
|
|
16
|
-
|
|
16
|
+
opts.schema = resolveAndValidateSchemaUID(opts.schema, 'schema UID');
|
|
17
17
|
validateBytes32(opts.uid, 'attestation UID');
|
|
18
18
|
|
|
19
19
|
const client = createEASClient(opts.chain, opts.rpcUrl);
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { createReadOnlyEASClient } from '../client.js';
|
|
3
3
|
import { output, handleError } from '../output.js';
|
|
4
|
-
import {
|
|
4
|
+
import { resolveAndValidateSchemaUID } from '../validation.js';
|
|
5
5
|
|
|
6
6
|
export const schemaGetCommand = new Command('schema-get')
|
|
7
7
|
.description('Get a schema by UID')
|
|
8
|
-
.requiredOption('-u, --uid <uid>', 'Schema UID')
|
|
8
|
+
.requiredOption('-u, --uid <uid>', 'Schema UID or popular schema name')
|
|
9
9
|
.option('-c, --chain <name>', 'Chain name', 'ethereum')
|
|
10
10
|
.option('--rpc-url <url>', 'Custom RPC URL')
|
|
11
11
|
.action(async (opts) => {
|
|
12
12
|
try {
|
|
13
|
-
|
|
13
|
+
opts.uid = resolveAndValidateSchemaUID(opts.uid, 'schema UID');
|
|
14
14
|
|
|
15
15
|
const client = createReadOnlyEASClient(opts.chain, opts.rpcUrl);
|
|
16
16
|
const schema = await client.schemaRegistry.getSchema({ uid: opts.uid });
|
package/src/commands/set-key.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { setStoredPrivateKey } from '../config.js';
|
|
|
4
4
|
import { output, handleError } from '../output.js';
|
|
5
5
|
|
|
6
6
|
export const setKeyCommand = new Command('set-key')
|
|
7
|
-
.description('Store your private key in ~/.
|
|
7
|
+
.description('Store your private key in ~/.easctl for future use')
|
|
8
8
|
.argument('<key>', 'Wallet private key (hex string, with or without 0x prefix)')
|
|
9
9
|
.action((key: string) => {
|
|
10
10
|
const normalized = key.startsWith('0x') ? key : `0x${key}`;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { ethers } from 'ethers';
|
|
3
|
+
import { CHAIN_CONFIGS, listChains } from '../chains.js';
|
|
4
|
+
import { output } from '../output.js';
|
|
5
|
+
import { getStoredPrivateKey } from '../config.js';
|
|
6
|
+
|
|
7
|
+
export const statusCommand = new Command('status')
|
|
8
|
+
.description('Show current configuration: wallet, chain, and contract addresses')
|
|
9
|
+
.option('-c, --chain <name>', 'Chain name', 'ethereum')
|
|
10
|
+
.option('--rpc-url <url>', 'Custom RPC URL')
|
|
11
|
+
.action(async (opts) => {
|
|
12
|
+
const key = process.env.EAS_PRIVATE_KEY || getStoredPrivateKey();
|
|
13
|
+
const source = process.env.EAS_PRIVATE_KEY ? 'env' : key ? 'stored' : undefined;
|
|
14
|
+
let address: string | undefined;
|
|
15
|
+
|
|
16
|
+
if (key) {
|
|
17
|
+
try {
|
|
18
|
+
const normalized = key.startsWith('0x') ? key : `0x${key}`;
|
|
19
|
+
address = new ethers.Wallet(normalized).address;
|
|
20
|
+
} catch {
|
|
21
|
+
// invalid key format
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const chainConfig = CHAIN_CONFIGS[opts.chain];
|
|
26
|
+
const rpcUrl = opts.rpcUrl || chainConfig?.defaultRpc || 'none';
|
|
27
|
+
|
|
28
|
+
output({
|
|
29
|
+
success: true,
|
|
30
|
+
data: {
|
|
31
|
+
wallet: {
|
|
32
|
+
privateKey: source ? (address ? `set (${source})` : `set (invalid format)`) : 'not set',
|
|
33
|
+
address: address || 'n/a',
|
|
34
|
+
},
|
|
35
|
+
chain: {
|
|
36
|
+
name: opts.chain,
|
|
37
|
+
chainId: chainConfig?.chainId ?? 'unknown chain',
|
|
38
|
+
rpcUrl,
|
|
39
|
+
},
|
|
40
|
+
contracts: chainConfig
|
|
41
|
+
? {
|
|
42
|
+
eas: chainConfig.eas,
|
|
43
|
+
schemaRegistry: chainConfig.schemaRegistry,
|
|
44
|
+
}
|
|
45
|
+
: 'unknown chain',
|
|
46
|
+
supportedChains: listChains().join(', '),
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
});
|
package/src/config.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -17,6 +17,8 @@ import { multiRevokeCommand } from './commands/multi-revoke.js';
|
|
|
17
17
|
import { multiTimestampCommand } from './commands/multi-timestamp.js';
|
|
18
18
|
import { setKeyCommand } from './commands/set-key.js';
|
|
19
19
|
import { clearKeyCommand } from './commands/clear-key.js';
|
|
20
|
+
import { popularSchemasCommand } from './commands/popular-schemas.js';
|
|
21
|
+
import { statusCommand } from './commands/status.js';
|
|
20
22
|
|
|
21
23
|
const program = new Command();
|
|
22
24
|
|
|
@@ -42,6 +44,7 @@ program.addCommand(getAttestationCommand);
|
|
|
42
44
|
// Schema commands
|
|
43
45
|
program.addCommand(schemaRegisterCommand);
|
|
44
46
|
program.addCommand(schemaGetCommand);
|
|
47
|
+
program.addCommand(popularSchemasCommand);
|
|
45
48
|
|
|
46
49
|
// Timestamp commands
|
|
47
50
|
program.addCommand(timestampCommand);
|
|
@@ -57,6 +60,9 @@ program.addCommand(querySchemasCommand);
|
|
|
57
60
|
program.addCommand(setKeyCommand);
|
|
58
61
|
program.addCommand(clearKeyCommand);
|
|
59
62
|
|
|
63
|
+
// Status command
|
|
64
|
+
program.addCommand(statusCommand);
|
|
65
|
+
|
|
60
66
|
// Chains command
|
|
61
67
|
program
|
|
62
68
|
.command('chains')
|