Skip to content

Commit acb6c5d

Browse files
committed
add broadcast command
1 parent cb0a66f commit acb6c5d

File tree

10 files changed

+825
-13
lines changed

10 files changed

+825
-13
lines changed

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
"/oclif.manifest.json"
1818
],
1919
"dependencies": {
20+
"@dialectlabs/sdk": "^0.2.0",
2021
"@oclif/core": "^1.9.5",
2122
"@oclif/plugin-help": "^5",
22-
"@oclif/plugin-plugins": "^2.1.0"
23+
"@oclif/plugin-plugins": "^2.1.0",
24+
"@solana/web3.js": "^1.47.1"
2325
},
2426
"devDependencies": {
2527
"@oclif/test": "^2",

src/commands/broadcast/index.ts

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { Backend, Dialect } from '@dialectlabs/sdk';
2+
import { CliUx, Command, Flags } from '@oclif/core';
3+
import {
4+
envFlag,
5+
keypairFlag,
6+
modeFlag,
7+
sdkEnvFromEnvFlag,
8+
} from '../../shared/flags';
9+
import { createWalletFromFile } from '../../shared/wallet';
10+
11+
export default class Broadcast extends Command {
12+
static description = 'Command sends messages to multiples accounts';
13+
14+
static examples = ['<%= config.bin %> <%= command.id %>'];
15+
16+
static flags = {
17+
title: Flags.string({
18+
char: 't',
19+
description: 'Message title',
20+
required: true,
21+
}),
22+
message: Flags.string({
23+
char: 'm',
24+
description: 'Message to send',
25+
required: true,
26+
}),
27+
mode: modeFlag,
28+
keypair: keypairFlag,
29+
env: envFlag,
30+
};
31+
32+
static args = [];
33+
34+
public async run(): Promise<void> {
35+
const { flags } = await this.parse(Broadcast);
36+
37+
const backends = [];
38+
if (flags.mode === 'cloud') {
39+
backends.push(Backend.DialectCloud);
40+
}
41+
42+
if (flags.mode === 'solana') {
43+
backends.push(Backend.Solana);
44+
}
45+
46+
const wallet = await createWalletFromFile(flags.keypair!);
47+
48+
const sdk = Dialect.sdk({
49+
wallet,
50+
environment: sdkEnvFromEnvFlag(flags.env),
51+
backends: backends,
52+
});
53+
54+
const dapp = await sdk.dapps.find();
55+
if (!dapp) {
56+
throw new Error(
57+
`You don't have dapp associated with your public key: ${sdk.wallet.publicKey}`,
58+
);
59+
}
60+
61+
this.log('Dapp public address:', dapp.publicKey.toString());
62+
63+
CliUx.ux.action.start('fetching addresses');
64+
65+
const addresses = await dapp.dappAddresses.findAll();
66+
67+
CliUx.ux.action.stop(`found ${addresses.length} addresses`);
68+
69+
if (addresses.length === 0) {
70+
return;
71+
}
72+
73+
const confirmed = await CliUx.ux.confirm(
74+
`Message preview:
75+
${flags.title}
76+
${flags.message}
77+
to ${addresses.length} addresses? [y/n]`,
78+
);
79+
80+
if (!confirmed) {
81+
return;
82+
}
83+
84+
CliUx.ux.action.start('sending message');
85+
86+
await dapp.messages.send({
87+
title: flags.title,
88+
message: flags.message,
89+
});
90+
91+
CliUx.ux.action.stop('sending message');
92+
}
93+
}

src/shared/flags/env.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Environment } from '@dialectlabs/sdk';
2+
import { Flags } from '@oclif/core';
3+
4+
export const EnvFlagOptions = {
5+
LocalDev: 'local-dev',
6+
Dev: 'dev',
7+
Production: 'prod',
8+
} as const;
9+
10+
export type EnvFlagOptionValues =
11+
typeof EnvFlagOptions[keyof typeof EnvFlagOptions];
12+
13+
export const envFlag = Flags.enum({
14+
options: [
15+
EnvFlagOptions.LocalDev,
16+
EnvFlagOptions.Dev,
17+
EnvFlagOptions.Production,
18+
],
19+
default: EnvFlagOptions.Production,
20+
});
21+
22+
export function sdkEnvFromEnvFlag(flagValue: EnvFlagOptionValues): Environment {
23+
switch (flagValue) {
24+
case EnvFlagOptions.LocalDev:
25+
return 'local-development';
26+
case EnvFlagOptions.Dev:
27+
return 'development';
28+
case EnvFlagOptions.Production:
29+
return 'production';
30+
}
31+
}

src/shared/flags/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './env';
2+
export * from './keypair';
3+
export * from './mode';

src/shared/flags/keypair.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Flags } from '@oclif/core';
2+
import path from 'node:path';
3+
4+
export const keypairFlag = Flags.file({
5+
char: 'k',
6+
description: 'Filepath to a keypair',
7+
default: process.env.HOME
8+
? path.resolve(process.env.HOME, '.config/solana/id.json')
9+
: 'id.json',
10+
required: true,
11+
});

src/shared/flags/mode.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { Flags } from '@oclif/core';
2+
3+
export const modeFlag = Flags.enum({
4+
options: ['cloud', 'solana'],
5+
default: 'cloud',
6+
});

src/shared/wallet.ts

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import {
2+
DialectWalletAdapter,
3+
NodeDialectWalletAdapter,
4+
} from '@dialectlabs/sdk';
5+
import { Keypair } from '@solana/web3.js';
6+
import { readFile } from 'node:fs/promises';
7+
8+
export async function keypairFromFile(keypairPath: string): Promise<Keypair> {
9+
const privateKey = await readFile(keypairPath);
10+
return Keypair.fromSecretKey(
11+
new Uint8Array(JSON.parse(privateKey.toString())),
12+
);
13+
}
14+
15+
export function createWalletFromKeypair(
16+
keypair: Keypair,
17+
): DialectWalletAdapter {
18+
return new NodeDialectWalletAdapter(keypair);
19+
}
20+
21+
export async function createWalletFromFile(
22+
keypairPath: string,
23+
): Promise<DialectWalletAdapter> {
24+
return createWalletFromKeypair(await keypairFromFile(keypairPath));
25+
}

test/commands/broadcast.test.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import {expect, test} from '@oclif/test'
2+
3+
describe('broadcast', () => {
4+
test
5+
.stdout()
6+
.command(['broadcast'])
7+
.it('runs hello', ctx => {
8+
expect(ctx.stdout).to.contain('hello world')
9+
})
10+
11+
test
12+
.stdout()
13+
.command(['broadcast', '--name', 'jeff'])
14+
.it('runs hello --name jeff', ctx => {
15+
expect(ctx.stdout).to.contain('hello jeff')
16+
})
17+
})

tsconfig.json

+3-4
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@
66
"outDir": "dist",
77
"rootDir": "src",
88
"strict": true,
9-
"target": "es2019"
9+
"target": "es2019",
10+
"esModuleInterop": true
1011
},
11-
"include": [
12-
"src/**/*"
13-
]
12+
"include": ["src/**/*"]
1413
}

0 commit comments

Comments
 (0)