@signet-base/cli 0.2.2 → 0.3.1
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/package.json +2 -2
- package/src/api.js +2 -16
- package/src/commands/estimate.js +11 -8
- package/src/commands/list.js +4 -38
- package/src/commands/post.js +13 -9
- package/src/contract.js +172 -0
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signet-base/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "CLI for Signet onchain advertising — list and post spotlight ads",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"signet": "./bin/signet.js",
|
|
8
|
-
"signet-
|
|
8
|
+
"signet-base": "./bin/signet.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"start": "node bin/signet.js",
|
package/src/api.js
CHANGED
|
@@ -1,25 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Signet API client
|
|
2
|
+
* Signet API client — only for x402 spotlight endpoint (requires HTTP for payment headers)
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const DEFAULT_BASE_URL = "https://signet.sebayaki.com";
|
|
6
6
|
|
|
7
|
-
export async function fetchEstimate(guaranteeHours = 0, baseUrl = DEFAULT_BASE_URL) {
|
|
8
|
-
const res = await fetch(`${baseUrl}/api/x402/estimate?guaranteeHours=${guaranteeHours}`);
|
|
9
|
-
if (!res.ok) throw new Error(`Estimate failed: ${res.status} ${await res.text()}`);
|
|
10
|
-
return res.json();
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export async function fetchSignatures(startIndex = 0, endIndex = 5, baseUrl = DEFAULT_BASE_URL) {
|
|
14
|
-
const res = await fetch(
|
|
15
|
-
`${baseUrl}/api/signature/list?startIndex=${startIndex}&endIndex=${endIndex}`
|
|
16
|
-
);
|
|
17
|
-
if (!res.ok) throw new Error(`List failed: ${res.status} ${await res.text()}`);
|
|
18
|
-
return res.json();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
7
|
/**
|
|
22
|
-
* POST to spotlight endpoint. Returns the raw Response for
|
|
8
|
+
* POST to spotlight endpoint. Returns the raw Response for x402 header handling.
|
|
23
9
|
*/
|
|
24
10
|
export async function postSpotlightRaw({ url, guaranteeHours, headers = {}, baseUrl = DEFAULT_BASE_URL }) {
|
|
25
11
|
return fetch(`${baseUrl}/api/x402/spotlight`, {
|
package/src/commands/estimate.js
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { estimateUSDC, getSpotlightStatus } from "../contract.js";
|
|
2
2
|
|
|
3
3
|
export async function estimate(opts) {
|
|
4
4
|
const hours = parseInt(opts.hours);
|
|
5
5
|
|
|
6
6
|
try {
|
|
7
|
-
const
|
|
7
|
+
const [priceData, spotlight] = await Promise.all([
|
|
8
|
+
estimateUSDC(hours),
|
|
9
|
+
getSpotlightStatus(),
|
|
10
|
+
]);
|
|
8
11
|
|
|
9
|
-
console.log(`\n💰 Signet Spotlight Estimate
|
|
10
|
-
console.log(` Guarantee Hours: ${
|
|
11
|
-
console.log(` Estimated Cost: $${
|
|
12
|
-
console.log(` Spotlight Available: ${
|
|
12
|
+
console.log(`\n💰 Signet Spotlight Estimate\n`);
|
|
13
|
+
console.log(` Guarantee Hours: ${hours}`);
|
|
14
|
+
console.log(` Estimated Cost: $${priceData.estimatedUSDC} USDC`);
|
|
15
|
+
console.log(` Spotlight Available: ${spotlight.spotlightAvailable ? "✅ Yes" : "❌ No"}`);
|
|
13
16
|
|
|
14
|
-
if (
|
|
15
|
-
const mins = Math.ceil(
|
|
17
|
+
if (spotlight.spotlightRemainingSeconds > 0) {
|
|
18
|
+
const mins = Math.ceil(spotlight.spotlightRemainingSeconds / 60);
|
|
16
19
|
console.log(` Current Guarantee Remaining: ${mins} min`);
|
|
17
20
|
}
|
|
18
21
|
|
package/src/commands/list.js
CHANGED
|
@@ -1,30 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { base } from "viem/chains";
|
|
3
|
-
import { fetchSignatures } from "../api.js";
|
|
4
|
-
|
|
5
|
-
const SIGNET_CONTRACT = "0xd53A6Ff418a5647704032089F64D9f0c5Ac958B0";
|
|
6
|
-
const SIGNET_ABI = [
|
|
7
|
-
{
|
|
8
|
-
inputs: [],
|
|
9
|
-
name: "getSignatureCount",
|
|
10
|
-
outputs: [{ type: "uint256" }],
|
|
11
|
-
stateMutability: "view",
|
|
12
|
-
type: "function",
|
|
13
|
-
},
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
async function getSignatureCount() {
|
|
17
|
-
const client = createPublicClient({
|
|
18
|
-
chain: base,
|
|
19
|
-
transport: http(),
|
|
20
|
-
});
|
|
21
|
-
const count = await client.readContract({
|
|
22
|
-
address: SIGNET_CONTRACT,
|
|
23
|
-
abi: SIGNET_ABI,
|
|
24
|
-
functionName: "getSignatureCount",
|
|
25
|
-
});
|
|
26
|
-
return Number(count);
|
|
27
|
-
}
|
|
1
|
+
import { getSignatureCount, getSignatureList } from "../contract.js";
|
|
28
2
|
|
|
29
3
|
export async function list(opts) {
|
|
30
4
|
const count = parseInt(opts.count);
|
|
@@ -37,15 +11,9 @@ export async function list(opts) {
|
|
|
37
11
|
return;
|
|
38
12
|
}
|
|
39
13
|
|
|
40
|
-
// Fetch the most recent signatures (0-indexed)
|
|
41
14
|
const latestIndex = total - 1;
|
|
42
15
|
const startIndex = Math.max(0, latestIndex - count + 1);
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
if (!signatures?.length) {
|
|
46
|
-
console.log("No signatures found.");
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
16
|
+
const signatures = await getSignatureList(startIndex, latestIndex);
|
|
49
17
|
|
|
50
18
|
// Sort descending (most recent first)
|
|
51
19
|
signatures.sort((a, b) => b.signatureIndex - a.signatureIndex);
|
|
@@ -55,11 +23,9 @@ export async function list(opts) {
|
|
|
55
23
|
for (const sig of signatures) {
|
|
56
24
|
const date = new Date(sig.timestamp * 1000).toLocaleDateString();
|
|
57
25
|
const hunt = (Number(sig.huntAmount) / 1e18).toFixed(2);
|
|
58
|
-
const title = sig.metadata?.title || "(no title)";
|
|
59
26
|
|
|
60
|
-
console.log(` #${sig.signatureIndex} — ${
|
|
61
|
-
console.log(`
|
|
62
|
-
console.log(` HUNT: ${hunt} | Views: ${sig.viewCount} | Clicks: ${sig.clickCount}`);
|
|
27
|
+
console.log(` #${sig.signatureIndex} — ${sig.url}`);
|
|
28
|
+
console.log(` HUNT: ${hunt} | Guarantee: ${sig.guaranteeHours}h`);
|
|
63
29
|
console.log(` Date: ${date} | Wallet: ${sig.userWallet.slice(0, 8)}...`);
|
|
64
30
|
console.log();
|
|
65
31
|
}
|
package/src/commands/post.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { x402Client, x402HTTPClient } from "@x402/core/client";
|
|
2
2
|
import { registerExactEvmScheme } from "@x402/evm/exact/client";
|
|
3
3
|
import { privateKeyToAccount } from "viem/accounts";
|
|
4
|
-
import {
|
|
4
|
+
import { estimateUSDC, getSpotlightStatus } from "../contract.js";
|
|
5
|
+
import { postSpotlightRaw } from "../api.js";
|
|
5
6
|
|
|
6
7
|
export async function post(opts) {
|
|
7
8
|
const privateKey = opts.privateKey || process.env.PRIVATE_KEY;
|
|
@@ -19,14 +20,17 @@ export async function post(opts) {
|
|
|
19
20
|
console.log(` Guarantee: ${hours}h\n`);
|
|
20
21
|
|
|
21
22
|
try {
|
|
22
|
-
// Step 0: Get estimate
|
|
23
|
-
console.log("0️⃣ Fetching estimate...");
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
// Step 0: Get estimate from chain
|
|
24
|
+
console.log("0️⃣ Fetching estimate (on-chain)...");
|
|
25
|
+
const [priceData, spotlight] = await Promise.all([
|
|
26
|
+
estimateUSDC(hours),
|
|
27
|
+
getSpotlightStatus(),
|
|
28
|
+
]);
|
|
29
|
+
console.log(` Estimated Cost: $${priceData.estimatedUSDC} USDC`);
|
|
30
|
+
console.log(` Available: ${spotlight.spotlightAvailable ? "Yes" : "No"}\n`);
|
|
31
|
+
|
|
32
|
+
if (!spotlight.spotlightAvailable) {
|
|
33
|
+
console.log(` ⚠️ Spotlight is currently guaranteed. ${Math.ceil(spotlight.spotlightRemainingSeconds / 60)} min remaining.`);
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
// Step 1: POST without payment to get 402 response
|
package/src/contract.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { createPublicClient, http, fallback, formatUnits } from "viem";
|
|
2
|
+
import { base } from "viem/chains";
|
|
3
|
+
|
|
4
|
+
const SIGNET_CONTRACT = "0xd53A6Ff418a5647704032089F64D9f0c5Ac958B0";
|
|
5
|
+
const ZAP_CONTRACT = "0x7321e0f77F69C6944C45be683b60265C17bd4a73";
|
|
6
|
+
const USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
7
|
+
|
|
8
|
+
const BASE_RPC_ENDPOINTS = [
|
|
9
|
+
"https://mainnet.base.org",
|
|
10
|
+
"https://base.llamarpc.com",
|
|
11
|
+
"https://base-rpc.publicnode.com",
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const SIGNET_ABI = [
|
|
15
|
+
{
|
|
16
|
+
inputs: [],
|
|
17
|
+
name: "getSignatureCount",
|
|
18
|
+
outputs: [{ type: "uint256" }],
|
|
19
|
+
stateMutability: "view",
|
|
20
|
+
type: "function",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
inputs: [{ name: "index", type: "uint256" }],
|
|
24
|
+
name: "getSignature",
|
|
25
|
+
outputs: [{
|
|
26
|
+
type: "tuple",
|
|
27
|
+
components: [
|
|
28
|
+
{ name: "timestamp", type: "uint48" },
|
|
29
|
+
{ name: "blockNumber", type: "uint48" },
|
|
30
|
+
{ name: "userWallet", type: "address" },
|
|
31
|
+
{ name: "url", type: "string" },
|
|
32
|
+
{ name: "huntAmount", type: "uint96" },
|
|
33
|
+
{ name: "signetAmount", type: "uint96" },
|
|
34
|
+
{ name: "guaranteeHours", type: "uint8" },
|
|
35
|
+
],
|
|
36
|
+
}],
|
|
37
|
+
stateMutability: "view",
|
|
38
|
+
type: "function",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
inputs: [
|
|
42
|
+
{ name: "startIndex", type: "uint256" },
|
|
43
|
+
{ name: "endIndex", type: "uint256" },
|
|
44
|
+
],
|
|
45
|
+
name: "getSignatureList",
|
|
46
|
+
outputs: [{
|
|
47
|
+
type: "tuple[]",
|
|
48
|
+
components: [
|
|
49
|
+
{ name: "timestamp", type: "uint48" },
|
|
50
|
+
{ name: "blockNumber", type: "uint48" },
|
|
51
|
+
{ name: "userWallet", type: "address" },
|
|
52
|
+
{ name: "url", type: "string" },
|
|
53
|
+
{ name: "huntAmount", type: "uint96" },
|
|
54
|
+
{ name: "signetAmount", type: "uint96" },
|
|
55
|
+
{ name: "guaranteeHours", type: "uint8" },
|
|
56
|
+
],
|
|
57
|
+
}],
|
|
58
|
+
stateMutability: "view",
|
|
59
|
+
type: "function",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
inputs: [],
|
|
63
|
+
name: "isSpotlightAvailable",
|
|
64
|
+
outputs: [{ type: "bool" }],
|
|
65
|
+
stateMutability: "view",
|
|
66
|
+
type: "function",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
inputs: [],
|
|
70
|
+
name: "getSpotlightRemainingTime",
|
|
71
|
+
outputs: [{ type: "uint256" }],
|
|
72
|
+
stateMutability: "view",
|
|
73
|
+
type: "function",
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
const ZAP_ABI = [
|
|
78
|
+
{
|
|
79
|
+
inputs: [
|
|
80
|
+
{ name: "inputToken", type: "address" },
|
|
81
|
+
{ name: "guaranteeHours", type: "uint8" },
|
|
82
|
+
],
|
|
83
|
+
name: "estimateSign",
|
|
84
|
+
outputs: [
|
|
85
|
+
{ name: "inputAmount", type: "uint256" },
|
|
86
|
+
{ name: "huntAmount", type: "uint256" },
|
|
87
|
+
],
|
|
88
|
+
stateMutability: "view",
|
|
89
|
+
type: "function",
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
function getClient() {
|
|
94
|
+
return createPublicClient({
|
|
95
|
+
chain: base,
|
|
96
|
+
transport: fallback(
|
|
97
|
+
BASE_RPC_ENDPOINTS.map(url => http(url, { timeout: 5000, retryCount: 1 })),
|
|
98
|
+
{ rank: false }
|
|
99
|
+
),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function getSignatureCount() {
|
|
104
|
+
const client = getClient();
|
|
105
|
+
const count = await client.readContract({
|
|
106
|
+
address: SIGNET_CONTRACT,
|
|
107
|
+
abi: SIGNET_ABI,
|
|
108
|
+
functionName: "getSignatureCount",
|
|
109
|
+
});
|
|
110
|
+
return Number(count);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export async function getSignatureList(startIndex, endIndex) {
|
|
114
|
+
const client = getClient();
|
|
115
|
+
// Contract's getSignatureList requires startIndex < endIndex
|
|
116
|
+
const sigs = await client.readContract({
|
|
117
|
+
address: SIGNET_CONTRACT,
|
|
118
|
+
abi: SIGNET_ABI,
|
|
119
|
+
functionName: "getSignatureList",
|
|
120
|
+
args: [BigInt(startIndex), BigInt(endIndex + 1)], // endIndex is exclusive in contract when startIndex < endIndex
|
|
121
|
+
});
|
|
122
|
+
return sigs.map((sig, i) => ({
|
|
123
|
+
signatureIndex: startIndex + i,
|
|
124
|
+
timestamp: Number(sig.timestamp),
|
|
125
|
+
blockNumber: Number(sig.blockNumber),
|
|
126
|
+
userWallet: sig.userWallet,
|
|
127
|
+
url: sig.url,
|
|
128
|
+
huntAmount: sig.huntAmount.toString(),
|
|
129
|
+
signetAmount: sig.signetAmount.toString(),
|
|
130
|
+
guaranteeHours: sig.guaranteeHours,
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export async function getSpotlightStatus() {
|
|
135
|
+
const client = getClient();
|
|
136
|
+
const [isAvailable, remainingTime] = await Promise.all([
|
|
137
|
+
client.readContract({
|
|
138
|
+
address: SIGNET_CONTRACT,
|
|
139
|
+
abi: SIGNET_ABI,
|
|
140
|
+
functionName: "isSpotlightAvailable",
|
|
141
|
+
}),
|
|
142
|
+
client.readContract({
|
|
143
|
+
address: SIGNET_CONTRACT,
|
|
144
|
+
abi: SIGNET_ABI,
|
|
145
|
+
functionName: "getSpotlightRemainingTime",
|
|
146
|
+
}),
|
|
147
|
+
]);
|
|
148
|
+
return {
|
|
149
|
+
spotlightAvailable: Boolean(isAvailable),
|
|
150
|
+
spotlightRemainingSeconds: Number(remainingTime),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export async function estimateUSDC(guaranteeHours) {
|
|
155
|
+
const client = getClient();
|
|
156
|
+
const PRICE_BUFFER_BPS = 500n; // 5% buffer matching server config
|
|
157
|
+
|
|
158
|
+
const result = await client.simulateContract({
|
|
159
|
+
address: ZAP_CONTRACT,
|
|
160
|
+
abi: ZAP_ABI,
|
|
161
|
+
functionName: "estimateSign",
|
|
162
|
+
args: [USDC_ADDRESS, guaranteeHours],
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const [usdcAmount] = result.result;
|
|
166
|
+
const usdcWithBuffer = (usdcAmount * (10000n + PRICE_BUFFER_BPS)) / 10000n;
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
estimatedUSDC: formatUnits(usdcWithBuffer, 6),
|
|
170
|
+
estimatedUSDCRaw: usdcWithBuffer.toString(),
|
|
171
|
+
};
|
|
172
|
+
}
|