bulletin-deploy 0.5.4 → 0.5.6
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 +18 -0
- package/bin/bulletin-deploy +7 -2
- package/cdm.json +60 -22
- package/dist/{chunk-2RURGSQW.js → chunk-4ZPXQDUZ.js} +10 -7
- package/dist/{chunk-OMZHBMOF.js → chunk-CXNOJQNX.js} +153 -124
- package/dist/{chunk-K2KKLHWN.js → chunk-YTCA5JTA.js} +5 -2
- package/dist/deploy.d.ts +11 -3
- package/dist/deploy.js +5 -3
- package/dist/dotns.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/telemetry.d.ts +2 -1
- package/dist/telemetry.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,6 +43,9 @@ bulletin-deploy ./dist my-app00.dot
|
|
|
43
43
|
|
|
44
44
|
# Custom RPC endpoint
|
|
45
45
|
bulletin-deploy --rpc wss://custom-bulletin.example.com ./dist my-app00.dot
|
|
46
|
+
|
|
47
|
+
# Deploy and publish to the Playground remix registry
|
|
48
|
+
bulletin-deploy --playground ./dist my-app00.dot
|
|
46
49
|
```
|
|
47
50
|
|
|
48
51
|
### All options
|
|
@@ -50,9 +53,24 @@ bulletin-deploy --rpc wss://custom-bulletin.example.com ./dist my-app00.dot
|
|
|
50
53
|
```
|
|
51
54
|
Options:
|
|
52
55
|
--rpc wss://... Bulletin RPC (or set BULLETIN_RPC env var)
|
|
56
|
+
--mnemonic "..." DotNS owner mnemonic (or set MNEMONIC env var)
|
|
57
|
+
--playground Publish to the Playground remix registry
|
|
58
|
+
--pool-size N Number of pool accounts (default: 10)
|
|
53
59
|
--help Show help
|
|
54
60
|
```
|
|
55
61
|
|
|
62
|
+
### Playground registry
|
|
63
|
+
|
|
64
|
+
By default, deploys only upload to Bulletin storage and register the DotNS domain. The **Playground remix registry** is an on-chain app directory that makes your deploy visible in [Polkadot Playground](https://playground.polkadot.cloud).
|
|
65
|
+
|
|
66
|
+
To publish to it, pass `--playground`:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
bulletin-deploy --playground ./dist my-app.dot
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
This requires `cdm.json` in your project root (shipped with bulletin-deploy) and a git remote origin.
|
|
73
|
+
|
|
56
74
|
## GitHub Actions
|
|
57
75
|
|
|
58
76
|
1. Copy `workflows/deploy-on-pr.yml` to your repo's `.github/workflows/` directory
|
package/bin/bulletin-deploy
CHANGED
|
@@ -13,6 +13,7 @@ for (let i = 0; i < args.length; i++) {
|
|
|
13
13
|
else if (args[i] === "--pool-size") { flags.poolSize = parseInt(args[++i], 10); }
|
|
14
14
|
else if (args[i] === "--mnemonic") { flags.mnemonic = args[++i]; }
|
|
15
15
|
else if (args[i] === "--rpc") { flags.rpc = args[++i]; }
|
|
16
|
+
else if (args[i] === "--playground") { flags.playground = true; }
|
|
16
17
|
else if (args[i] === "--help" || args[i] === "-h") { flags.help = true; }
|
|
17
18
|
else { positional.push(args[i]); }
|
|
18
19
|
}
|
|
@@ -26,6 +27,7 @@ Options:
|
|
|
26
27
|
--mnemonic "..." DotNS owner mnemonic (or set MNEMONIC env var)
|
|
27
28
|
--rpc wss://... Bulletin RPC (or set BULLETIN_RPC env var)
|
|
28
29
|
--pool-size N Number of pool accounts (default: 10)
|
|
30
|
+
--playground Publish to the playground remix registry
|
|
29
31
|
--help Show this help`);
|
|
30
32
|
process.exit(0);
|
|
31
33
|
}
|
|
@@ -37,14 +39,17 @@ try {
|
|
|
37
39
|
if (flags.bootstrap) {
|
|
38
40
|
const rpc = process.env.BULLETIN_RPC || "wss://paseo-bulletin-rpc.polkadot.io";
|
|
39
41
|
const poolSize = parseInt(process.env.BULLETIN_POOL_SIZE || "10", 10);
|
|
40
|
-
await bootstrapPool(rpc, poolSize);
|
|
42
|
+
await bootstrapPool(rpc, poolSize, flags.mnemonic);
|
|
41
43
|
} else {
|
|
42
44
|
const [buildDir, domain] = positional;
|
|
43
45
|
if (!buildDir) { console.error("Error: build directory required"); process.exit(1); }
|
|
44
46
|
if (!domain) { console.error("Error: domain required (e.g. my-app.dot)"); process.exit(1); }
|
|
45
47
|
if (!fs.existsSync(buildDir)) { console.error(`Error: ${buildDir} does not exist`); process.exit(1); }
|
|
46
48
|
|
|
47
|
-
const result = await deploy(buildDir, domain
|
|
49
|
+
const result = await deploy(buildDir, domain, {
|
|
50
|
+
playground: flags.playground,
|
|
51
|
+
mnemonic: flags.mnemonic,
|
|
52
|
+
});
|
|
48
53
|
|
|
49
54
|
const output = process.env.GITHUB_OUTPUT;
|
|
50
55
|
if (output) {
|
package/cdm.json
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
"contracts": {
|
|
14
14
|
"acc2c3b5e912b762": {
|
|
15
15
|
"@example/playground-registry": {
|
|
16
|
-
"version":
|
|
17
|
-
"address": "
|
|
16
|
+
"version": 3,
|
|
17
|
+
"address": "0xF8304E5C17769A53E012f39c6990cC8a7AaBC6A5",
|
|
18
18
|
"abi": [
|
|
19
19
|
{
|
|
20
20
|
"type": "constructor",
|
|
@@ -39,35 +39,58 @@
|
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
41
|
"type": "function",
|
|
42
|
-
"name": "
|
|
42
|
+
"name": "getAppCount",
|
|
43
|
+
"inputs": [],
|
|
44
|
+
"outputs": [
|
|
45
|
+
{
|
|
46
|
+
"name": "",
|
|
47
|
+
"type": "uint32"
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
"stateMutability": "view"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"type": "function",
|
|
54
|
+
"name": "getDomainAt",
|
|
43
55
|
"inputs": [
|
|
44
56
|
{
|
|
45
|
-
"name": "
|
|
57
|
+
"name": "index",
|
|
58
|
+
"type": "uint32"
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
"outputs": [
|
|
62
|
+
{
|
|
63
|
+
"name": "",
|
|
46
64
|
"type": "string"
|
|
47
65
|
}
|
|
48
66
|
],
|
|
67
|
+
"stateMutability": "view"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"type": "function",
|
|
71
|
+
"name": "getOwnerAppCount",
|
|
72
|
+
"inputs": [
|
|
73
|
+
{
|
|
74
|
+
"name": "owner",
|
|
75
|
+
"type": "address"
|
|
76
|
+
}
|
|
77
|
+
],
|
|
49
78
|
"outputs": [
|
|
50
79
|
{
|
|
51
80
|
"name": "",
|
|
52
|
-
"type": "
|
|
53
|
-
"components": [
|
|
54
|
-
{
|
|
55
|
-
"name": "isSome",
|
|
56
|
-
"type": "bool"
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
"name": "value",
|
|
60
|
-
"type": "string"
|
|
61
|
-
}
|
|
62
|
-
]
|
|
81
|
+
"type": "uint32"
|
|
63
82
|
}
|
|
64
83
|
],
|
|
65
84
|
"stateMutability": "view"
|
|
66
85
|
},
|
|
67
86
|
{
|
|
68
87
|
"type": "function",
|
|
69
|
-
"name": "
|
|
88
|
+
"name": "getOwnerDomainAt",
|
|
70
89
|
"inputs": [
|
|
90
|
+
{
|
|
91
|
+
"name": "owner",
|
|
92
|
+
"type": "address"
|
|
93
|
+
},
|
|
71
94
|
{
|
|
72
95
|
"name": "index",
|
|
73
96
|
"type": "uint32"
|
|
@@ -83,7 +106,7 @@
|
|
|
83
106
|
},
|
|
84
107
|
{
|
|
85
108
|
"type": "function",
|
|
86
|
-
"name": "
|
|
109
|
+
"name": "getMetadataUri",
|
|
87
110
|
"inputs": [
|
|
88
111
|
{
|
|
89
112
|
"name": "domain",
|
|
@@ -93,25 +116,40 @@
|
|
|
93
116
|
"outputs": [
|
|
94
117
|
{
|
|
95
118
|
"name": "",
|
|
96
|
-
"type": "
|
|
119
|
+
"type": "tuple",
|
|
120
|
+
"components": [
|
|
121
|
+
{
|
|
122
|
+
"name": "isSome",
|
|
123
|
+
"type": "bool"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"name": "value",
|
|
127
|
+
"type": "string"
|
|
128
|
+
}
|
|
129
|
+
]
|
|
97
130
|
}
|
|
98
131
|
],
|
|
99
132
|
"stateMutability": "view"
|
|
100
133
|
},
|
|
101
134
|
{
|
|
102
135
|
"type": "function",
|
|
103
|
-
"name": "
|
|
104
|
-
"inputs": [
|
|
136
|
+
"name": "getOwner",
|
|
137
|
+
"inputs": [
|
|
138
|
+
{
|
|
139
|
+
"name": "domain",
|
|
140
|
+
"type": "string"
|
|
141
|
+
}
|
|
142
|
+
],
|
|
105
143
|
"outputs": [
|
|
106
144
|
{
|
|
107
145
|
"name": "",
|
|
108
|
-
"type": "
|
|
146
|
+
"type": "address"
|
|
109
147
|
}
|
|
110
148
|
],
|
|
111
149
|
"stateMutability": "view"
|
|
112
150
|
}
|
|
113
151
|
],
|
|
114
|
-
"metadataCid": "
|
|
152
|
+
"metadataCid": "bafk2bzacebkch7cuoi77j5vkrkoplax4urywwga7ijamtd3pntv7xjgabhtgs"
|
|
115
153
|
}
|
|
116
154
|
}
|
|
117
155
|
}
|
|
@@ -56,6 +56,9 @@ function getDeployAttributes(domain) {
|
|
|
56
56
|
"deploy.pr": process.env.GITHUB_PR_NUMBER || void 0
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
+
function isExpectedError(msg) {
|
|
60
|
+
return /personhood|owned by|owner mismatch|reserved for original|invalid domain label|not authorized for bulletin/i.test(msg);
|
|
61
|
+
}
|
|
59
62
|
async function withSpan(op, description, attributes, fn) {
|
|
60
63
|
if (!Sentry) return fn();
|
|
61
64
|
return Sentry.startSpan({ op, name: description, attributes }, async (span) => {
|
|
@@ -63,9 +66,7 @@ async function withSpan(op, description, attributes, fn) {
|
|
|
63
66
|
return await fn();
|
|
64
67
|
} catch (error) {
|
|
65
68
|
span.setAttribute("error.message", error.message);
|
|
66
|
-
|
|
67
|
-
Sentry.captureException(error);
|
|
68
|
-
error._sentryCaptured = true;
|
|
69
|
+
span.setStatus({ code: 2, message: "internal_error" });
|
|
69
70
|
throw error;
|
|
70
71
|
}
|
|
71
72
|
});
|
|
@@ -84,11 +85,12 @@ async function withDeploySpan(domain, fn) {
|
|
|
84
85
|
try {
|
|
85
86
|
return await fn();
|
|
86
87
|
} catch (error) {
|
|
88
|
+
const msg = error.message;
|
|
87
89
|
span.setAttribute("deploy.status", "error");
|
|
88
|
-
span.setAttribute("deploy.error",
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
span.setAttribute("deploy.error", msg);
|
|
91
|
+
const isExpected = isExpectedError(msg);
|
|
92
|
+
if (!isExpected) {
|
|
93
|
+
span.setStatus({ code: 2, message: "internal_error" });
|
|
92
94
|
}
|
|
93
95
|
throw error;
|
|
94
96
|
}
|
|
@@ -118,6 +120,7 @@ async function flush() {
|
|
|
118
120
|
export {
|
|
119
121
|
initTelemetry,
|
|
120
122
|
resolveRepo,
|
|
123
|
+
isExpectedError,
|
|
121
124
|
withSpan,
|
|
122
125
|
withDeploySpan,
|
|
123
126
|
setDeployAttribute,
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
DotNS,
|
|
4
4
|
TX_TIMEOUT_MS,
|
|
5
5
|
fetchNonce
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-YTCA5JTA.js";
|
|
7
7
|
import {
|
|
8
8
|
derivePoolAccounts,
|
|
9
9
|
ensureAuthorized,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
setDeployAttribute,
|
|
17
17
|
withDeploySpan,
|
|
18
18
|
withSpan
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-4ZPXQDUZ.js";
|
|
20
20
|
|
|
21
21
|
// src/deploy.ts
|
|
22
22
|
import { Buffer } from "buffer";
|
|
@@ -26,7 +26,7 @@ import { fileURLToPath } from "url";
|
|
|
26
26
|
import { execSync } from "child_process";
|
|
27
27
|
import { sha256 } from "@noble/hashes/sha256";
|
|
28
28
|
import { blake2b } from "@noble/hashes/blake2b";
|
|
29
|
-
import { createClient as createPolkadotClient } from "polkadot-api";
|
|
29
|
+
import { createClient as createPolkadotClient, Enum } from "polkadot-api";
|
|
30
30
|
import { Binary } from "@polkadot-api/substrate-bindings";
|
|
31
31
|
import { getWsProvider } from "polkadot-api/ws-provider";
|
|
32
32
|
import { withPolkadotSdkCompat } from "polkadot-api/polkadot-sdk-compat";
|
|
@@ -43,7 +43,6 @@ import { sr25519CreateDerive } from "@polkadot-labs/hdkd";
|
|
|
43
43
|
import { mnemonicToEntropy, entropyToMiniSecret, ss58Address } from "@polkadot-labs/hdkd-helpers";
|
|
44
44
|
var BULLETIN_RPC = process.env.BULLETIN_RPC || "wss://paseo-bulletin-rpc.polkadot.io";
|
|
45
45
|
var POOL_SIZE = parseInt(process.env.BULLETIN_POOL_SIZE || "10", 10);
|
|
46
|
-
var POOL_MNEMONIC = process.env.BULLETIN_POOL_MNEMONIC || void 0;
|
|
47
46
|
var CHUNK_SIZE = 1 * 1024 * 1024;
|
|
48
47
|
var MAX_FILE_SIZE = 8 * 1024 * 1024;
|
|
49
48
|
var CID_CONFIG = { version: 1, codec: 85, hashCode: 18, hashLength: 32 };
|
|
@@ -60,15 +59,18 @@ function getGitRemoteUrl() {
|
|
|
60
59
|
return null;
|
|
61
60
|
}
|
|
62
61
|
}
|
|
63
|
-
function
|
|
64
|
-
const mnemonic = process.env.DOTNS_MNEMONIC || process.env.MNEMONIC || DEFAULT_MNEMONIC;
|
|
62
|
+
function deriveRootSigner(mnemonic) {
|
|
65
63
|
const entropy = mnemonicToEntropy(mnemonic);
|
|
66
64
|
const miniSecret = entropyToMiniSecret(entropy);
|
|
67
65
|
const derive = sr25519CreateDerive(miniSecret);
|
|
68
66
|
const keyPair = derive("");
|
|
69
67
|
const signer = getPolkadotSigner(keyPair.publicKey, "Sr25519", keyPair.sign);
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
return { signer, ss58: ss58Address(keyPair.publicKey) };
|
|
69
|
+
}
|
|
70
|
+
function getRegistrySigner(explicitMnemonic) {
|
|
71
|
+
const mnemonic = explicitMnemonic || process.env.DOTNS_MNEMONIC || process.env.MNEMONIC || DEFAULT_MNEMONIC;
|
|
72
|
+
const { signer, ss58 } = deriveRootSigner(mnemonic);
|
|
73
|
+
return { signer, origin: ss58 };
|
|
72
74
|
}
|
|
73
75
|
function createCID(data, codec = CID_CONFIG.codec, hashCode = CID_CONFIG.hashCode) {
|
|
74
76
|
let hash;
|
|
@@ -103,7 +105,8 @@ async function getProvider() {
|
|
|
103
105
|
const client = createPolkadotClient(withPolkadotSdkCompat(getWsProvider(BULLETIN_RPC)));
|
|
104
106
|
const unsafeApi = client.getUnsafeApi();
|
|
105
107
|
await cryptoWaitReady();
|
|
106
|
-
const
|
|
108
|
+
const poolMnemonic = process.env.BULLETIN_POOL_MNEMONIC || void 0;
|
|
109
|
+
const poolAccounts = derivePoolAccounts(POOL_SIZE, poolMnemonic);
|
|
107
110
|
const authorizations = await fetchPoolAuthorizations(unsafeApi, poolAccounts);
|
|
108
111
|
let selected = selectAccount(authorizations);
|
|
109
112
|
if (!selected) {
|
|
@@ -115,10 +118,31 @@ async function getProvider() {
|
|
|
115
118
|
await ensureAuthorized(unsafeApi, selected, BULLETIN_RPC);
|
|
116
119
|
}
|
|
117
120
|
console.log(` Using pool account ${selected.index}: ${selected.address}`);
|
|
121
|
+
setDeployAttribute("deploy.signer.mode", "pool");
|
|
118
122
|
setDeployAttribute("deploy.pool.account", selected.address);
|
|
119
123
|
setDeployAttribute("deploy.pool.index", selected.index);
|
|
120
124
|
return { client, unsafeApi, signer: selected.signer, ss58: selected.address };
|
|
121
125
|
}
|
|
126
|
+
async function getDirectProvider(mnemonic) {
|
|
127
|
+
console.log(` Connecting to Bulletin: ${BULLETIN_RPC}`);
|
|
128
|
+
const client = createPolkadotClient(withPolkadotSdkCompat(getWsProvider(BULLETIN_RPC)));
|
|
129
|
+
const unsafeApi = client.getUnsafeApi();
|
|
130
|
+
const { signer, ss58 } = deriveRootSigner(mnemonic);
|
|
131
|
+
console.log(` Using direct signer: ${ss58}`);
|
|
132
|
+
const auth = await unsafeApi.query.TransactionStorage.Authorizations.getValue(
|
|
133
|
+
Enum("Account", ss58)
|
|
134
|
+
);
|
|
135
|
+
const txsRemaining = auth ? BigInt(auth.extent.transactions) : 0n;
|
|
136
|
+
const bytesRemaining = auth ? auth.extent.bytes : 0n;
|
|
137
|
+
if (txsRemaining === 0n && bytesRemaining === 0n) {
|
|
138
|
+
client.destroy();
|
|
139
|
+
throw new Error(`Account ${ss58} is not authorized for Bulletin storage. Run 'bulletin-deploy --bootstrap --mnemonic "..."' or authorize the account on-chain first.`);
|
|
140
|
+
}
|
|
141
|
+
console.log(` Authorization: ${txsRemaining} txs, ${Number(bytesRemaining) / 1e6}MB remaining`);
|
|
142
|
+
setDeployAttribute("deploy.signer.mode", "direct");
|
|
143
|
+
setDeployAttribute("deploy.signer.address", ss58);
|
|
144
|
+
return { client, unsafeApi, signer, ss58 };
|
|
145
|
+
}
|
|
122
146
|
function watchTransaction(tx, signer, txOpts, onSuccess, { label = "transaction", rpc, senderSS58, expectedNonce } = {}) {
|
|
123
147
|
return new Promise((resolve2, reject) => {
|
|
124
148
|
let settled = false;
|
|
@@ -348,7 +372,7 @@ async function merkleize(directoryPath, outputCarPath) {
|
|
|
348
372
|
console.log(` CAR: ${(size / 1024 / 1024).toFixed(2)} MB`);
|
|
349
373
|
return { carPath: outputCarPath, cid };
|
|
350
374
|
}
|
|
351
|
-
async function storeDirectory(directoryPath) {
|
|
375
|
+
async function storeDirectory(directoryPath, provider = {}) {
|
|
352
376
|
const carPath = path.join(path.dirname(directoryPath), `${path.basename(directoryPath)}.car`);
|
|
353
377
|
const { cid: ipfsCid } = await withSpan("deploy.merkleize", "1a. merkleize", { "deploy.directory": directoryPath }, async () => {
|
|
354
378
|
return merkleize(directoryPath, carPath);
|
|
@@ -356,11 +380,11 @@ async function storeDirectory(directoryPath) {
|
|
|
356
380
|
const carBuffer = fs.readFileSync(carPath);
|
|
357
381
|
const carChunks = chunk(carBuffer, CHUNK_SIZE);
|
|
358
382
|
const storageCid = await withSpan("deploy.chunk-upload", "1b. chunk-upload", { "deploy.chunks.total": carChunks.length, "deploy.car.bytes": carBuffer.length }, async () => {
|
|
359
|
-
return storeChunkedContent(carChunks);
|
|
383
|
+
return storeChunkedContent(carChunks, provider);
|
|
360
384
|
});
|
|
361
385
|
return { storageCid, ipfsCid };
|
|
362
386
|
}
|
|
363
|
-
async function deploy(content, domainName = null) {
|
|
387
|
+
async function deploy(content, domainName = null, options = {}) {
|
|
364
388
|
initTelemetry();
|
|
365
389
|
const randomSuffix = Math.floor(Math.random() * 100).toString().padStart(2, "0");
|
|
366
390
|
const name = domainName ? domainName.replace(".dot", "") : `test-domain-${Date.now().toString(36)}${randomSuffix}`;
|
|
@@ -372,134 +396,139 @@ async function deploy(content, domainName = null) {
|
|
|
372
396
|
console.log("=".repeat(60));
|
|
373
397
|
console.log(` Domain: ${name}.dot`);
|
|
374
398
|
if (typeof content === "string") console.log(` Build dir: ${path.resolve(content)}`);
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
399
|
+
const provider = options.mnemonic ? await getDirectProvider(options.mnemonic) : await getProvider();
|
|
400
|
+
try {
|
|
401
|
+
console.log("\n" + "=".repeat(60));
|
|
402
|
+
console.log("Storage");
|
|
403
|
+
console.log("=".repeat(60));
|
|
404
|
+
await withSpan("deploy.storage", "1. storage", {}, async () => {
|
|
405
|
+
if (process.env.IPFS_CID) {
|
|
406
|
+
cid = process.env.IPFS_CID;
|
|
407
|
+
ipfsCid = cid;
|
|
408
|
+
console.log(`
|
|
383
409
|
Using CID: ${cid}`);
|
|
384
|
-
|
|
385
|
-
console.log(`
|
|
386
|
-
Mode: Multi-chunk (${content.length} chunks)`);
|
|
387
|
-
cid = await storeChunkedContent(content);
|
|
388
|
-
} else if (typeof content === "string") {
|
|
389
|
-
const contentPath = path.resolve(content);
|
|
390
|
-
if (!fs.existsSync(contentPath)) throw new Error(`Path not found: ${contentPath}`);
|
|
391
|
-
const stats = fs.statSync(contentPath);
|
|
392
|
-
if (stats.isDirectory()) {
|
|
410
|
+
} else if (Array.isArray(content)) {
|
|
393
411
|
console.log(`
|
|
412
|
+
Mode: Multi-chunk (${content.length} chunks)`);
|
|
413
|
+
cid = await storeChunkedContent(content, provider);
|
|
414
|
+
} else if (typeof content === "string") {
|
|
415
|
+
const contentPath = path.resolve(content);
|
|
416
|
+
if (!fs.existsSync(contentPath)) throw new Error(`Path not found: ${contentPath}`);
|
|
417
|
+
const stats = fs.statSync(contentPath);
|
|
418
|
+
if (stats.isDirectory()) {
|
|
419
|
+
console.log(`
|
|
394
420
|
Mode: Directory`);
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
421
|
+
console.log(` Path: ${contentPath}`);
|
|
422
|
+
const result = await storeDirectory(contentPath, provider);
|
|
423
|
+
cid = result.storageCid;
|
|
424
|
+
ipfsCid = result.ipfsCid;
|
|
425
|
+
} else {
|
|
426
|
+
console.log(`
|
|
401
427
|
Mode: File`);
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
428
|
+
console.log(` Path: ${contentPath}`);
|
|
429
|
+
const fileContent = fs.readFileSync(contentPath);
|
|
430
|
+
if (fileContent.length > MAX_FILE_SIZE) {
|
|
431
|
+
console.log(` Exceeds 8MB, chunking...`);
|
|
432
|
+
cid = await storeChunkedContent(chunk(fileContent), provider);
|
|
433
|
+
} else {
|
|
434
|
+
cid = await storeFile(new Uint8Array(fileContent), provider);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
} else if (content instanceof Uint8Array) {
|
|
438
|
+
console.log(`
|
|
439
|
+
Mode: Bytes`);
|
|
440
|
+
if (content.length > MAX_FILE_SIZE) {
|
|
405
441
|
console.log(` Exceeds 8MB, chunking...`);
|
|
406
|
-
cid = await storeChunkedContent(chunk(
|
|
442
|
+
cid = await storeChunkedContent(chunk(content), provider);
|
|
407
443
|
} else {
|
|
408
|
-
cid = await storeFile(
|
|
444
|
+
cid = await storeFile(content, provider);
|
|
409
445
|
}
|
|
446
|
+
} else {
|
|
447
|
+
throw new Error("Invalid content: must be path, Uint8Array, or Array<Uint8Array>");
|
|
410
448
|
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
449
|
+
});
|
|
450
|
+
setDeployAttribute("deploy.cid", cid);
|
|
451
|
+
console.log("\n" + "=".repeat(60));
|
|
452
|
+
console.log("DotNS");
|
|
453
|
+
console.log("=".repeat(60));
|
|
454
|
+
await withSpan("deploy.dotns", "2. dotns", { "deploy.domain": name }, async () => {
|
|
455
|
+
const dotns = new DotNS();
|
|
456
|
+
await dotns.connect(options.mnemonic ? { mnemonic: options.mnemonic } : {});
|
|
457
|
+
const { owned, owner } = await dotns.checkOwnership(name);
|
|
458
|
+
if (owned) {
|
|
459
|
+
console.log(` Status: Already owned`);
|
|
460
|
+
} else if (owner && owner !== "0x0000000000000000000000000000000000000000") {
|
|
461
|
+
throw new Error(`Domain ${name}.dot is owned by ${owner}, not ${dotns.evmAddress}`);
|
|
417
462
|
} else {
|
|
418
|
-
|
|
463
|
+
console.log(` Status: Registering...`);
|
|
464
|
+
await dotns.register(name);
|
|
419
465
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
console.log(` Status: Already owned`);
|
|
434
|
-
} else if (owner && owner !== "0x0000000000000000000000000000000000000000") {
|
|
435
|
-
throw new Error(`Domain ${name}.dot is owned by ${owner}, not ${dotns.evmAddress}`);
|
|
436
|
-
} else {
|
|
437
|
-
console.log(` Status: Registering...`);
|
|
438
|
-
await dotns.register(name);
|
|
439
|
-
}
|
|
440
|
-
const contenthashHex = `0x${encodeContenthash(cid)}`;
|
|
441
|
-
await dotns.setContenthash(name, contenthashHex);
|
|
442
|
-
dotns.disconnect();
|
|
443
|
-
});
|
|
444
|
-
console.log("\n" + "=".repeat(60));
|
|
445
|
-
console.log("Registry");
|
|
446
|
-
console.log("=".repeat(60));
|
|
447
|
-
await withSpan("deploy.registry", "3. registry", { "deploy.domain": name }, async () => {
|
|
448
|
-
const repoUrl = getGitRemoteUrl();
|
|
449
|
-
if (!repoUrl) {
|
|
450
|
-
console.log("\n Skipping registry: not a git repository (no remote origin found)");
|
|
451
|
-
return;
|
|
452
|
-
}
|
|
453
|
-
if (!fs.existsSync(CDM_JSON_PATH)) {
|
|
454
|
-
console.log("\n Skipping registry: cdm.json not found");
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
457
|
-
console.log(`
|
|
458
|
-
Repository: ${repoUrl}`);
|
|
459
|
-
const metadata = JSON.stringify({ repository: repoUrl });
|
|
460
|
-
const metadataBytes = new Uint8Array(Buffer.from(metadata, "utf-8"));
|
|
461
|
-
console.log(` Uploading metadata to Bulletin...`);
|
|
462
|
-
const metadataCid = await storeFile(metadataBytes);
|
|
463
|
-
console.log(` Metadata CID: ${metadataCid}`);
|
|
464
|
-
const cdmJson = JSON.parse(fs.readFileSync(CDM_JSON_PATH, "utf-8"));
|
|
465
|
-
const { signer, origin } = getRegistrySigner();
|
|
466
|
-
console.log(` Publishing to registry as ${origin}...`);
|
|
467
|
-
const MAX_REGISTRY_RETRIES = 3;
|
|
468
|
-
for (let attempt = 1; attempt <= MAX_REGISTRY_RETRIES; attempt++) {
|
|
469
|
-
const cdm = createCdm(cdmJson, { defaultSigner: signer, defaultOrigin: origin });
|
|
470
|
-
try {
|
|
471
|
-
const registry = cdm.getContract("@example/playground-registry");
|
|
472
|
-
const result = await registry.publish.tx(`${name}.dot`, metadataCid);
|
|
473
|
-
if (!result.ok) throw new Error("Registry publish transaction failed");
|
|
474
|
-
console.log(` Tx: ${result.txHash}`);
|
|
475
|
-
console.log(` Registered ${name}.dot in app registry!`);
|
|
476
|
-
break;
|
|
477
|
-
} catch (e) {
|
|
478
|
-
if (attempt < MAX_REGISTRY_RETRIES) {
|
|
479
|
-
captureWarning("Registry publish failed, retrying", { attempt, maxRetries: MAX_REGISTRY_RETRIES, error: e.message?.slice(0, 200) });
|
|
480
|
-
console.log(` Attempt ${attempt} failed: ${e.message?.slice(0, 80)}`);
|
|
481
|
-
console.log(` Retrying in 6s...`);
|
|
482
|
-
await new Promise((r) => setTimeout(r, 6e3));
|
|
483
|
-
continue;
|
|
466
|
+
const contenthashHex = `0x${encodeContenthash(cid)}`;
|
|
467
|
+
await dotns.setContenthash(name, contenthashHex);
|
|
468
|
+
dotns.disconnect();
|
|
469
|
+
});
|
|
470
|
+
if (options.playground) {
|
|
471
|
+
console.log("\n" + "=".repeat(60));
|
|
472
|
+
console.log("Playground Registry");
|
|
473
|
+
console.log("=".repeat(60));
|
|
474
|
+
await withSpan("deploy.registry", "3. registry", { "deploy.domain": name }, async () => {
|
|
475
|
+
const repoUrl = getGitRemoteUrl();
|
|
476
|
+
if (!fs.existsSync(CDM_JSON_PATH)) {
|
|
477
|
+
console.log("\n Skipping registry: cdm.json not found");
|
|
478
|
+
return;
|
|
484
479
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
480
|
+
const metadata = {};
|
|
481
|
+
if (repoUrl) metadata.repository = repoUrl;
|
|
482
|
+
console.log(`
|
|
483
|
+
Metadata: ${JSON.stringify(metadata)}`);
|
|
484
|
+
const metadataBytes = new Uint8Array(Buffer.from(JSON.stringify(metadata), "utf-8"));
|
|
485
|
+
console.log(` Uploading metadata to Bulletin...`);
|
|
486
|
+
const metadataCid = await storeFile(metadataBytes, provider);
|
|
487
|
+
console.log(` Metadata CID: ${metadataCid}`);
|
|
488
|
+
const cdmJson = JSON.parse(fs.readFileSync(CDM_JSON_PATH, "utf-8"));
|
|
489
|
+
const { signer, origin } = getRegistrySigner(options.mnemonic);
|
|
490
|
+
console.log(` Publishing to registry as ${origin}...`);
|
|
491
|
+
const MAX_REGISTRY_RETRIES = 3;
|
|
492
|
+
for (let attempt = 1; attempt <= MAX_REGISTRY_RETRIES; attempt++) {
|
|
493
|
+
const cdm = createCdm(cdmJson, { defaultSigner: signer, defaultOrigin: origin });
|
|
494
|
+
try {
|
|
495
|
+
const registry = cdm.getContract("@example/playground-registry");
|
|
496
|
+
const result = await registry.publish.tx(`${name}.dot`, metadataCid);
|
|
497
|
+
if (!result.ok) throw new Error("Registry publish transaction failed");
|
|
498
|
+
console.log(` Tx: ${result.txHash}`);
|
|
499
|
+
console.log(` Registered ${name}.dot in playground registry!`);
|
|
500
|
+
break;
|
|
501
|
+
} catch (e) {
|
|
502
|
+
if (attempt < MAX_REGISTRY_RETRIES) {
|
|
503
|
+
captureWarning("Registry publish failed, retrying", { attempt, maxRetries: MAX_REGISTRY_RETRIES, error: e.message?.slice(0, 200) });
|
|
504
|
+
console.log(` Attempt ${attempt} failed: ${e.message?.slice(0, 80)}`);
|
|
505
|
+
console.log(` Retrying in 6s...`);
|
|
506
|
+
await new Promise((r) => setTimeout(r, 6e3));
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
throw e;
|
|
510
|
+
} finally {
|
|
511
|
+
cdm.destroy();
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
});
|
|
489
515
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
516
|
+
console.log("\n" + "=".repeat(60));
|
|
517
|
+
console.log("DEPLOYMENT COMPLETE!");
|
|
518
|
+
console.log("=".repeat(60));
|
|
519
|
+
console.log("\n\u{1F53A} Polkadot Triangle");
|
|
520
|
+
console.log(` - Polkadot Desktop: ${name}.dot`);
|
|
521
|
+
console.log(` - Polkadot Browser: https://${name}.dot.li`);
|
|
522
|
+
console.log("\n" + "=".repeat(60) + "\n");
|
|
523
|
+
return { domainName: name, fullDomain: `${name}.dot`, cid, ipfsCid };
|
|
524
|
+
} finally {
|
|
525
|
+
provider.client.destroy();
|
|
526
|
+
}
|
|
499
527
|
});
|
|
500
528
|
}
|
|
501
529
|
|
|
502
530
|
export {
|
|
531
|
+
deriveRootSigner,
|
|
503
532
|
createCID,
|
|
504
533
|
encodeContenthash,
|
|
505
534
|
storeFile,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
captureWarning,
|
|
3
3
|
withSpan
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4ZPXQDUZ.js";
|
|
5
5
|
|
|
6
6
|
// src/dotns.ts
|
|
7
7
|
import crypto from "crypto";
|
|
@@ -607,7 +607,10 @@ var DotNS = class {
|
|
|
607
607
|
}
|
|
608
608
|
async register(label, options = {}) {
|
|
609
609
|
return withSpan("deploy.dotns.register", `2a. register ${label}.dot`, {}, async () => {
|
|
610
|
-
const
|
|
610
|
+
const MAINNET_RPC_PATTERNS = ["polkadot-asset-hub-rpc.polkadot.io", "kusama-asset-hub-rpc.polkadot.io", "polkadothub-rpc.com/mainnet"];
|
|
611
|
+
const isMainnet = this.rpc && MAINNET_RPC_PATTERNS.some((p) => this.rpc.includes(p));
|
|
612
|
+
const statusDefault = isMainnet ? "none" : "full";
|
|
613
|
+
const status = parseProofOfPersonhoodStatus(options.status || process.env.DOTNS_STATUS || statusDefault);
|
|
611
614
|
const reverse = options.reverse ?? (process.env.DOTNS_REVERSE ?? "false").toLowerCase() === "true";
|
|
612
615
|
if (!this.connected) await this.connect(options);
|
|
613
616
|
label = validateDomainLabel(label);
|
package/dist/deploy.d.ts
CHANGED
|
@@ -14,6 +14,10 @@ interface ExistingProvider {
|
|
|
14
14
|
signer?: PolkadotSigner;
|
|
15
15
|
ss58?: string;
|
|
16
16
|
}
|
|
17
|
+
declare function deriveRootSigner(mnemonic: string): {
|
|
18
|
+
signer: PolkadotSigner;
|
|
19
|
+
ss58: string;
|
|
20
|
+
};
|
|
17
21
|
declare function createCID(data: Uint8Array, codec?: number, hashCode?: number): CID;
|
|
18
22
|
declare function encodeContenthash(cidString: string): string;
|
|
19
23
|
declare function storeFile(contentBytes: Uint8Array, { client: existingClient, unsafeApi: existingApi, signer: existingSigner }?: ExistingProvider): Promise<string>;
|
|
@@ -24,10 +28,14 @@ declare function merkleize(directoryPath: string, outputCarPath: string): Promis
|
|
|
24
28
|
carPath: string;
|
|
25
29
|
cid: string;
|
|
26
30
|
}>;
|
|
27
|
-
declare function storeDirectory(directoryPath: string): Promise<{
|
|
31
|
+
declare function storeDirectory(directoryPath: string, provider?: ExistingProvider): Promise<{
|
|
28
32
|
storageCid: string;
|
|
29
33
|
ipfsCid: string;
|
|
30
34
|
}>;
|
|
31
|
-
|
|
35
|
+
interface DeployOptions {
|
|
36
|
+
playground?: boolean;
|
|
37
|
+
mnemonic?: string;
|
|
38
|
+
}
|
|
39
|
+
declare function deploy(content: DeployContent, domainName?: string | null, options?: DeployOptions): Promise<DeployResult>;
|
|
32
40
|
|
|
33
|
-
export { type DeployContent, type DeployResult, chunk, createCID, deploy, encodeContenthash, hasIPFS, merkleize, storeChunkedContent, storeDirectory, storeFile };
|
|
41
|
+
export { type DeployContent, type DeployOptions, type DeployResult, chunk, createCID, deploy, deriveRootSigner, encodeContenthash, hasIPFS, merkleize, storeChunkedContent, storeDirectory, storeFile };
|
package/dist/deploy.js
CHANGED
|
@@ -2,21 +2,23 @@ import {
|
|
|
2
2
|
chunk,
|
|
3
3
|
createCID,
|
|
4
4
|
deploy,
|
|
5
|
+
deriveRootSigner,
|
|
5
6
|
encodeContenthash,
|
|
6
7
|
hasIPFS,
|
|
7
8
|
merkleize,
|
|
8
9
|
storeChunkedContent,
|
|
9
10
|
storeDirectory,
|
|
10
11
|
storeFile
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-CXNOJQNX.js";
|
|
13
|
+
import "./chunk-YTCA5JTA.js";
|
|
13
14
|
import "./chunk-RV7XBIIO.js";
|
|
14
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-4ZPXQDUZ.js";
|
|
15
16
|
import "./chunk-QGM4M3NI.js";
|
|
16
17
|
export {
|
|
17
18
|
chunk,
|
|
18
19
|
createCID,
|
|
19
20
|
deploy,
|
|
21
|
+
deriveRootSigner,
|
|
20
22
|
encodeContenthash,
|
|
21
23
|
hasIPFS,
|
|
22
24
|
merkleize,
|
package/dist/dotns.js
CHANGED
|
@@ -18,8 +18,8 @@ import {
|
|
|
18
18
|
sanitizeDomainLabel,
|
|
19
19
|
stripTrailingDigits,
|
|
20
20
|
validateDomainLabel
|
|
21
|
-
} from "./chunk-
|
|
22
|
-
import "./chunk-
|
|
21
|
+
} from "./chunk-YTCA5JTA.js";
|
|
22
|
+
import "./chunk-4ZPXQDUZ.js";
|
|
23
23
|
import "./chunk-QGM4M3NI.js";
|
|
24
24
|
export {
|
|
25
25
|
CONTRACTS,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { DeployContent, DeployResult, deploy } from './deploy.js';
|
|
1
|
+
export { DeployContent, DeployOptions, DeployResult, deploy } from './deploy.js';
|
|
2
2
|
export { PoolAccount, PoolAuthorization, bootstrapPool, derivePoolAccounts, ensureAuthorized, fetchPoolAuthorizations, selectAccount } from './pool.js';
|
|
3
3
|
export { DotNS, DotNSConnectOptions, OwnershipResult, PriceValidationResult } from './dotns.js';
|
|
4
4
|
import 'multiformats/cid';
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
deploy
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-CXNOJQNX.js";
|
|
4
4
|
import {
|
|
5
5
|
DotNS
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-YTCA5JTA.js";
|
|
7
7
|
import {
|
|
8
8
|
bootstrapPool,
|
|
9
9
|
derivePoolAccounts,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
fetchPoolAuthorizations,
|
|
12
12
|
selectAccount
|
|
13
13
|
} from "./chunk-RV7XBIIO.js";
|
|
14
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-4ZPXQDUZ.js";
|
|
15
15
|
import "./chunk-QGM4M3NI.js";
|
|
16
16
|
export {
|
|
17
17
|
DotNS,
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
declare function initTelemetry(): void;
|
|
2
2
|
declare function resolveRepo(domain: string): string;
|
|
3
|
+
declare function isExpectedError(msg: string): boolean;
|
|
3
4
|
declare function withSpan<T>(op: string, description: string, attributes: Record<string, string | number | boolean | undefined>, fn: () => T | Promise<T>): Promise<T>;
|
|
4
5
|
declare function withDeploySpan<T>(domain: string, fn: () => T | Promise<T>): Promise<T>;
|
|
5
6
|
declare function setDeployAttribute(key: string, value: string | number | boolean): void;
|
|
6
7
|
declare function captureWarning(message: string, context?: Record<string, unknown>): void;
|
|
7
8
|
declare function flush(): Promise<void>;
|
|
8
9
|
|
|
9
|
-
export { captureWarning, flush, initTelemetry, resolveRepo, setDeployAttribute, withDeploySpan, withSpan };
|
|
10
|
+
export { captureWarning, flush, initTelemetry, isExpectedError, resolveRepo, setDeployAttribute, withDeploySpan, withSpan };
|
package/dist/telemetry.js
CHANGED
|
@@ -2,16 +2,18 @@ import {
|
|
|
2
2
|
captureWarning,
|
|
3
3
|
flush,
|
|
4
4
|
initTelemetry,
|
|
5
|
+
isExpectedError,
|
|
5
6
|
resolveRepo,
|
|
6
7
|
setDeployAttribute,
|
|
7
8
|
withDeploySpan,
|
|
8
9
|
withSpan
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-4ZPXQDUZ.js";
|
|
10
11
|
import "./chunk-QGM4M3NI.js";
|
|
11
12
|
export {
|
|
12
13
|
captureWarning,
|
|
13
14
|
flush,
|
|
14
15
|
initTelemetry,
|
|
16
|
+
isExpectedError,
|
|
15
17
|
resolveRepo,
|
|
16
18
|
setDeployAttribute,
|
|
17
19
|
withDeploySpan,
|