mofu-sdk 0.1.1 → 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 +13 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +59 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -82,6 +82,19 @@ if (event.type === "owner_changed") pause(event.token!.address);
|
|
|
82
82
|
|
|
83
83
|
`parseWebhook` throws when the signature is wrong or older than five minutes; `verifyWebhook` returns a boolean instead.
|
|
84
84
|
|
|
85
|
+
## Read a verdict on-chain
|
|
86
|
+
|
|
87
|
+
Mofu publishes verdicts to its MofuVerdicts registry on Robinhood Chain. Read one straight from the chain, without Mofu's API limits:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
const v = await mofu.onchainVerdict(token); // finds the registry once via mofu.works
|
|
91
|
+
v.verdict; // "clean" | "caution" | "risky" | null (not published)
|
|
92
|
+
v.flags.sellFailed; // also honeypotCode, canMint, canPause, canBlock, upgradeable, fakeMofu
|
|
93
|
+
v.scannedAt; // Date | null
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Pass `{ registry, rpcUrl }` to skip the lookup or use your own node.
|
|
97
|
+
|
|
85
98
|
## Errors
|
|
86
99
|
|
|
87
100
|
Failures throw `MofuError` with `status` and `code`: `daily_limit` (429), `bad_address` (400), `chain_unreachable` (503), `network` (Mofu couldn't be reached).
|
package/dist/index.d.ts
CHANGED
|
@@ -39,6 +39,14 @@ export declare class Mofu {
|
|
|
39
39
|
scanToken(address: string): Promise<Checked<TokenReport>>;
|
|
40
40
|
/** Wallet Health: every active approval (risky ones flagged) and the tokens a wallet holds. */
|
|
41
41
|
checkWallet(address: string): Promise<Checked<WalletReport>>;
|
|
42
|
+
/**
|
|
43
|
+
* A token's verdict as published on Robinhood Chain (the MofuVerdicts registry), read straight
|
|
44
|
+
* from the chain with one eth_call. Pass `registry` to skip asking mofu.works where it is.
|
|
45
|
+
*/
|
|
46
|
+
onchainVerdict(token: string, options?: {
|
|
47
|
+
registry?: string;
|
|
48
|
+
rpcUrl?: string;
|
|
49
|
+
}): Promise<OnchainVerdict>;
|
|
42
50
|
/** Scans a token and applies a buy rule. Says no, with a reason, when Mofu can't be reached. */
|
|
43
51
|
checkBeforeBuy(address: string, policy?: Partial<Policy>): Promise<Decision>;
|
|
44
52
|
readonly webhooks: {
|
|
@@ -98,3 +106,21 @@ export declare function parseWebhook(signatureHeader: string | null | undefined,
|
|
|
98
106
|
toleranceSeconds?: number;
|
|
99
107
|
now?: number;
|
|
100
108
|
}): Promise<WebhookEvent>;
|
|
109
|
+
export interface OnchainVerdict {
|
|
110
|
+
registry: string;
|
|
111
|
+
/** null when Mofu hasn't published one for this token. */
|
|
112
|
+
verdict: "clean" | "caution" | "risky" | null;
|
|
113
|
+
flags: {
|
|
114
|
+
sellFailed: boolean;
|
|
115
|
+
honeypotCode: boolean;
|
|
116
|
+
canMint: boolean;
|
|
117
|
+
canPause: boolean;
|
|
118
|
+
canBlock: boolean;
|
|
119
|
+
upgradeable: boolean;
|
|
120
|
+
fakeMofu: boolean;
|
|
121
|
+
};
|
|
122
|
+
scannedAt: Date | null;
|
|
123
|
+
fingerprint: string | null;
|
|
124
|
+
}
|
|
125
|
+
/** Decodes verdictOf(address) → (uint8 verdict, uint8 flags, uint40 scannedAt, bytes16 fingerprint). */
|
|
126
|
+
export declare function decodeOnchainVerdict(result: string, registry: string): OnchainVerdict;
|
package/dist/index.js
CHANGED
|
@@ -64,6 +64,41 @@ export class Mofu {
|
|
|
64
64
|
checkWallet(address) {
|
|
65
65
|
return this.call(`/api/v1/wallet/${Mofu.address(address, "address")}`);
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* A token's verdict as published on Robinhood Chain (the MofuVerdicts registry), read straight
|
|
69
|
+
* from the chain with one eth_call. Pass `registry` to skip asking mofu.works where it is.
|
|
70
|
+
*/
|
|
71
|
+
async onchainVerdict(token, options = {}) {
|
|
72
|
+
const address = Mofu.address(token, "token");
|
|
73
|
+
let registry = options.registry;
|
|
74
|
+
if (!registry) {
|
|
75
|
+
const info = await this.call("/api/v1/onchain");
|
|
76
|
+
if (!info.registry)
|
|
77
|
+
throw new MofuError("Mofu isn't publishing verdicts on-chain yet", 404, "not_published");
|
|
78
|
+
registry = info.registry;
|
|
79
|
+
}
|
|
80
|
+
const res = await this.fetchFn(options.rpcUrl ?? "https://robinhood-rpc.publicnode.com", {
|
|
81
|
+
method: "POST",
|
|
82
|
+
headers: { "Content-Type": "application/json" },
|
|
83
|
+
body: JSON.stringify({
|
|
84
|
+
jsonrpc: "2.0",
|
|
85
|
+
id: 1,
|
|
86
|
+
method: "eth_call",
|
|
87
|
+
// verdictOf(address)
|
|
88
|
+
params: [
|
|
89
|
+
{ to: registry, data: `0x371ad2ae${address.slice(2).padStart(64, "0")}` },
|
|
90
|
+
"latest",
|
|
91
|
+
],
|
|
92
|
+
}),
|
|
93
|
+
signal: AbortSignal.timeout(this.timeoutMs),
|
|
94
|
+
}).catch((err) => {
|
|
95
|
+
throw new MofuError(`Couldn't reach Robinhood Chain: ${err.message}`, 0, "network");
|
|
96
|
+
});
|
|
97
|
+
const body = (await res.json().catch(() => null));
|
|
98
|
+
if (!body?.result || body.result.length < 2 + 64 * 4)
|
|
99
|
+
throw new MofuError("Unexpected answer from the chain", 502, "bad_rpc");
|
|
100
|
+
return decodeOnchainVerdict(body.result, registry);
|
|
101
|
+
}
|
|
67
102
|
/** Scans a token and applies a buy rule. Says no, with a reason, when Mofu can't be reached. */
|
|
68
103
|
async checkBeforeBuy(address, policy = {}) {
|
|
69
104
|
try {
|
|
@@ -170,3 +205,27 @@ export async function parseWebhook(signatureHeader, rawBody, secret, options) {
|
|
|
170
205
|
throw new MofuError("The webhook signature doesn't match", 401, "bad_signature");
|
|
171
206
|
return JSON.parse(rawBody);
|
|
172
207
|
}
|
|
208
|
+
/** Decodes verdictOf(address) → (uint8 verdict, uint8 flags, uint40 scannedAt, bytes16 fingerprint). */
|
|
209
|
+
export function decodeOnchainVerdict(result, registry) {
|
|
210
|
+
const words = result.replace(/^0x/, "").match(/.{64}/g) ?? [];
|
|
211
|
+
const n = (i) => BigInt(`0x${words[i] ?? "0"}`);
|
|
212
|
+
const code = Number(n(0));
|
|
213
|
+
const f = Number(n(1));
|
|
214
|
+
const at = Number(n(2));
|
|
215
|
+
const fp = (words[3] ?? "").slice(0, 32);
|
|
216
|
+
return {
|
|
217
|
+
registry,
|
|
218
|
+
verdict: code === 1 ? "clean" : code === 2 ? "caution" : code === 3 ? "risky" : null,
|
|
219
|
+
flags: {
|
|
220
|
+
sellFailed: !!(f & 1),
|
|
221
|
+
honeypotCode: !!(f & 2),
|
|
222
|
+
canMint: !!(f & 4),
|
|
223
|
+
canPause: !!(f & 8),
|
|
224
|
+
canBlock: !!(f & 16),
|
|
225
|
+
upgradeable: !!(f & 32),
|
|
226
|
+
fakeMofu: !!(f & 64),
|
|
227
|
+
},
|
|
228
|
+
scannedAt: at ? new Date(at * 1000) : null,
|
|
229
|
+
fingerprint: /^0+$/.test(fp) || !fp ? null : fp,
|
|
230
|
+
};
|
|
231
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mofu-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Robinhood Chain token and wallet checks for agents, bots and scripts: Mofu's Token Scanner, Wallet Health, webhooks and a buy rule.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://mofu.works/docs/#sdk",
|