Skip to content
This repository was archived by the owner on Oct 12, 2025. It is now read-only.

Commit 18c1420

Browse files
committed
playground
1 parent 62f95e4 commit 18c1420

File tree

16 files changed

+327
-65
lines changed

16 files changed

+327
-65
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,5 @@ yarn-error.log*
3636
# Misc
3737
.DS_Store
3838
*.pem
39+
*.tsbuildinfo
40+
*.tsbuildinfo

packages/snap-playground/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# @ecency/snap-playground
2+
3+
Simple web app for installing and exercising the Ecency MetaMask Snap during
4+
local development.
5+
6+
## Usage
7+
8+
```bash
9+
yarn workspace @ecency/snap-playground start
10+
```
11+
12+
Open `http://localhost:5173` in a MetaMask Flask-enabled browser and use the
13+
page controls to install the snap, set a mnemonic and request derived addresses
14+
for all supported chains. The playground performs the Hive RPC lookup so any
15+
linked account name is displayed alongside the derived public keys.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>Ecency Snap Playground</title>
6+
</head>
7+
<body>
8+
<h1>Ecency Snap Playground</h1>
9+
<button id="connect">Install Snap</button>
10+
<input id="mnemonic" placeholder="mnemonic" size="80" />
11+
<button id="setMnemonic">Set Mnemonic</button>
12+
<button id="getAddresses">Get Addresses</button>
13+
<pre id="log"></pre>
14+
<script type="module" src="/src/main.ts"></script>
15+
</body>
16+
</html>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "@ecency/snap-playground",
3+
"private": true,
4+
"version": "0.1.0",
5+
"type": "module",
6+
"scripts": {
7+
"start": "yarn workspace @ecency/snap build && vite",
8+
"build": "yarn workspace @ecency/snap build && vite build"
9+
},
10+
"devDependencies": {
11+
"typescript": "^5.8.2",
12+
"vite": "^6.2.0"
13+
}
14+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
const snapId = `local:${window.location.origin}/snap.manifest.json`;
2+
3+
async function connect() {
4+
await (window as any).ethereum.request({
5+
method: "wallet_requestSnaps",
6+
params: { [snapId]: {} },
7+
});
8+
log("Snap installed");
9+
}
10+
11+
async function invoke(method: string, params?: any) {
12+
return (window as any).ethereum.request({
13+
method: "wallet_invokeSnap",
14+
params: {
15+
snapId,
16+
request: { method, params },
17+
},
18+
});
19+
}
20+
21+
function log(msg: string) {
22+
const el = document.getElementById("log");
23+
if (el) el.textContent += `${msg}\n`;
24+
}
25+
26+
async function lookupHiveAccount(pubkey: string): Promise<string | null> {
27+
try {
28+
const res = await fetch("https://api.hive.blog", {
29+
method: "POST",
30+
headers: { "content-type": "application/json" },
31+
body: JSON.stringify({
32+
jsonrpc: "2.0",
33+
id: 1,
34+
method: "account_by_key_api.get_key_references",
35+
params: { keys: [pubkey] },
36+
}),
37+
});
38+
const json = await res.json();
39+
return json?.result?.accounts?.[0]?.[0] ?? null;
40+
} catch {
41+
return null;
42+
}
43+
}
44+
45+
document.getElementById("connect")?.addEventListener("click", () => {
46+
connect().catch((err) => log(err.message));
47+
});
48+
49+
document.getElementById("setMnemonic")?.addEventListener("click", async () => {
50+
const mnemonic = (document.getElementById("mnemonic") as HTMLInputElement).value;
51+
try {
52+
await invoke("initialize", { mnemonic });
53+
log("Mnemonic stored");
54+
} catch (err) {
55+
log((err as Error).message);
56+
}
57+
});
58+
59+
document.getElementById("getAddresses")?.addEventListener("click", async () => {
60+
try {
61+
const res = await invoke("getAddresses");
62+
log(`BTC: ${res.btc}`);
63+
log(`ETH: ${res.eth}`);
64+
log(`APT: ${res.apt}`);
65+
log(`TRX: ${res.trx}`);
66+
log(`ATOM: ${res.atom}`);
67+
log(`SOL: ${res.sol}`);
68+
const account = await lookupHiveAccount(res.hive.active);
69+
if (account) {
70+
log(`HIVE account: ${account}`);
71+
} else {
72+
log(`HIVE owner: ${res.hive.owner}`);
73+
log(`HIVE active: ${res.hive.active}`);
74+
log(`HIVE posting: ${res.hive.posting}`);
75+
log(`HIVE memo: ${res.hive.memo}`);
76+
}
77+
} catch (err) {
78+
log((err as Error).message);
79+
}
80+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"extends": "../../tsconfig.json",
3+
"compilerOptions": {
4+
"rootDir": "src"
5+
},
6+
"include": ["src"]
7+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { defineConfig } from "vite";
2+
3+
export default defineConfig({
4+
publicDir: "../snap",
5+
server: {
6+
fs: {
7+
allow: [".."],
8+
},
9+
},
10+
});

packages/snap/README.md

Lines changed: 70 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,32 +37,68 @@ The build outputs `snap.manifest.json` alongside `dist/bundle.js`.
3737
The snap now appears in the MetaMask Snaps list and can be invoked by dApps
3838
using `local:@ecency/snap`.
3939

40+
### Playground
41+
42+
For a quick way to install and interact with the snap locally, start the
43+
included playground which builds the snap and serves an example page:
44+
45+
```bash
46+
yarn workspace @ecency/snap-playground start
47+
```
48+
49+
Open `http://localhost:5173` in a MetaMask Flask-enabled browser and use the
50+
page buttons to install the snap, set a mnemonic and request derived addresses
51+
for all supported chains. The playground resolves any Hive account name on your
52+
behalf and shows the public keys returned by the snap.
53+
4054
## Usage
4155

4256
The snap exposes several RPC methods:
4357

4458
- `initialize` – store a BIP39 mnemonic inside the snap.
4559
- `unlock` – validate and unlock previously stored mnemonic.
46-
- `getAddress` – return a public address for a given chain (`HIVE`, `BTC`, `ETH`, etc.).
60+
- `getAddresses` – derive public keys for Hive roles and addresses for BTC,
61+
ETH, APT, TRX, ATOM and SOL. dApps can perform any Hive account lookups
62+
themselves using the returned keys.
4763
- `signHiveTx` – sign a Hive transaction with the active key.
4864
- `signExternalTx` – sign transactions for external chains via `signExternalTx` from
4965
`@ecency/wallets`.
5066
- `getBalance` – query balances using `useGetExternalWalletBalanceQuery` (todo: integrate).
5167

52-
Example from a dApp:
53-
54-
```ts
55-
const result = await window.ethereum.request({
56-
method: "wallet_invokeSnap",
57-
params: {
58-
snapId: "local:@ecency/snap",
59-
request: {
60-
method: "getAddress",
61-
params: { chain: "HIVE" },
62-
},
63-
},
64-
});
65-
```
68+
### Connecting from a dApp
69+
70+
1. Request the snap to be installed in MetaMask. For local development use
71+
`local:@ecency/snap`; when published to npm replace it with `npm:@ecency/snap` and an
72+
optional version range.
73+
74+
```ts
75+
await window.ethereum.request({
76+
method: "wallet_enable",
77+
params: [{
78+
wallet_snap: {
79+
"local:@ecency/snap": {}
80+
}
81+
}]
82+
});
83+
```
84+
85+
2. Invoke RPC methods exposed by the snap:
86+
87+
```ts
88+
const result = await window.ethereum.request({
89+
method: "wallet_invokeSnap",
90+
params: {
91+
snapId: "local:@ecency/snap",
92+
request: { method: "getAddresses" }
93+
}
94+
});
95+
// Optional: resolve Hive account name
96+
const account = await lookupHiveAccount(result.hive.active);
97+
```
98+
99+
3. Use the returned data in your application. When integrating into Ecency.com, these
100+
calls can be wrapped in a connector module that detects MetaMask and manages snap
101+
installation.
66102

67103
## Required Permissions
68104

@@ -82,10 +118,28 @@ stored in memory or exposed to the client.
82118

83119
- Keys are derived only when required and cleared from memory immediately after
84120
use.
85-
- No network requests are made.
121+
- No network requests are made by the snap itself.
86122
- All transaction data is validated before processing.
87123
- No sensitive data is stored in browser storage.
88124
- The mnemonic phrase resides in the snap's managed state. Although snaps run in
89125
an isolated environment, that state persists on the user's machine. Avoid
90126
exposing the mnemonic and consider encrypting state for production
91127
deployments.
128+
129+
## Publishing & Discovery
130+
131+
To prepare the snap for public use:
132+
133+
1. **Build** the bundle and manifest:
134+
135+
```bash
136+
yarn workspace @ecency/snap build
137+
```
138+
139+
2. **Publish** the package to npm from `packages/snap`.
140+
141+
3. **Submit** the npm package to the [MetaMask Snaps Directory](https://docs.metamask.io/snaps/developing/register/) for
142+
listing and discovery.
143+
144+
Once published, dApps like Ecency.com can enable the snap using the `npm:@ecency/snap`
145+
identifier in the `wallet_enable` request.

packages/snap/build-manifest.cjs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ const pkg = JSON.parse(
66
fs.readFileSync(path.join(__dirname, "package.json"), "utf8"),
77
);
88
const bundlePath = path.join(__dirname, "dist", "bundle.js");
9+
// Compute the digest from the exact bundle bytes so the manifest always
10+
// matches what MetaMask downloads over HTTP.
911
const source = fs.readFileSync(bundlePath);
10-
const shasum = crypto.createHash("sha256").update(source).digest("hex");
12+
const shasum = crypto.createHash("sha256").update(source).digest("base64");
1113

1214
const manifest = {
1315
version: pkg.version,
@@ -17,7 +19,11 @@ const manifest = {
1719
source: {
1820
shasum,
1921
location: {
20-
local: "dist/bundle.js",
22+
npm: {
23+
filePath: "dist/bundle.js",
24+
packageName: pkg.name,
25+
registry: "https://registry.npmjs.org",
26+
},
2127
},
2228
},
2329
initialPermissions: {

packages/snap/snap.manifest.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
"proposedName": "Ecency Snap",
44
"description": "MetaMask Snap providing multi-chain wallet capabilities.",
55
"source": {
6-
"shasum": "7a7e92d25e072e85429c7288e9292058191192cac551341ae3fab60579154618",
6+
"shasum": "+gn3cFrS+R+J+5IBVFADiePNDRkejucH5jyiRiIEnf4=",
77
"location": {
8-
"local": "dist/bundle.js"
8+
"npm": {
9+
"filePath": "dist/bundle.js",
10+
"packageName": "@ecency/snap",
11+
"registry": "https://registry.npmjs.org"
12+
}
913
}
1014
},
1115
"initialPermissions": {
@@ -21,4 +25,4 @@
2125
"endowment:webassembly": {}
2226
},
2327
"manifestVersion": "0.1"
24-
}
28+
}

0 commit comments

Comments
 (0)