Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/@magic-ext/subtensor/.lintstagedrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
'*.{ts,tsx}':
- eslint --fix
1 change: 1 addition & 0 deletions packages/@magic-ext/subtensor/.prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('../../../.prettierrc.js');
7 changes: 7 additions & 0 deletions packages/@magic-ext/subtensor/babel.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"env": {
"test": {
"plugins": ["@babel/plugin-transform-modules-commonjs"]
}
}
}
21 changes: 21 additions & 0 deletions packages/@magic-ext/subtensor/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import rootEslintConfig from '../../../eslint.config.mjs';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export default [
...rootEslintConfig,
{
ignores: ['node_modules', 'coverage', 'dist', 'eslint.config.mjs', 'jest.config.ts'],
},
{
languageOptions: {
parserOptions: {
project: ['./tsconfig.json', './test/tsconfig.json'],
tsconfigRootDir: __dirname,
},
},
},
];
13 changes: 13 additions & 0 deletions packages/@magic-ext/subtensor/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import baseJestConfig from '../../../jest.config';
import type { Config } from '@jest/types';

const config: Config.InitialOptions = {
...baseJestConfig,
transform: {
'^.+\\.(js|jsx)$': 'babel-jest',
'\\.(ts|tsx)$': 'ts-jest',
},
coveragePathIgnorePatterns: ['index.cdn.ts', 'index.native.ts'],
};

export default config;
33 changes: 33 additions & 0 deletions packages/@magic-ext/subtensor/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@magic-ext/subtensor",
"version": "1.0.0",
"description": "magic subtensor extension",
"author": "Magic <team@magic.link> (https://magic.link/)",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/magiclabs/magic-js"
},
"files": [
"dist"
],
"target": "neutral",
"cdnGlobalName": "MagicSubtensorExtension",
"main": "./dist/cjs/index.js",
"module": "./dist/es/index.js",
"types": "./dist/types/index.d.ts",
"jsdelivr": "./dist/extension.js",
"exports": {
"import": "./dist/es/index.mjs",
"types": "./dist/types/index.d.ts",
"require": "./dist/cjs/index.js"
},
"externals": {
"include": [
"@magic-sdk/provider"
]
},
"devDependencies": {
"@magic-sdk/provider": "^33.7.1"
}
}
3 changes: 3 additions & 0 deletions packages/@magic-ext/subtensor/src/index.cdn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { SubtensorExtension } from './index';

export default SubtensorExtension;
1 change: 1 addition & 0 deletions packages/@magic-ext/subtensor/src/index.native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './index';
99 changes: 99 additions & 0 deletions packages/@magic-ext/subtensor/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { MultichainExtension } from '@magic-sdk/provider';

import type {
AddProxyParams,
AddStakeParams,
MoveStakeParams,
RemoveStakeParams,
SignRawParams,
SignedRawResult,
SignedTransactionResult,
SubtensorConfig,
TransferParams,
} from './type';

export class SubtensorExtension extends MultichainExtension<'subtensor'> {
name = 'subtensor' as const;

constructor(public subtensorConfig: SubtensorConfig) {
super({
rpcUrl: subtensorConfig.rpcUrl,
chainType: 'SUBTENSOR',
});
}

public getAccount = () => {
return this.request<string>({
id: 42,
jsonrpc: '2.0',
method: 'subtensor_getAccount',
params: [],
});
};

public sendTransaction = (params: TransferParams) => {
return this.request<SignedTransactionResult>({
id: 42,
jsonrpc: '2.0',
method: 'subtensor_sendTransaction',
params,
});
};

public addProxy = (params: AddProxyParams) => {
return this.request<SignedTransactionResult>({
id: 42,
jsonrpc: '2.0',
method: 'subtensor_addProxy',
params,
});
};

public addStake = (params: AddStakeParams) => {
return this.request<SignedTransactionResult>({
id: 42,
jsonrpc: '2.0',
method: 'subtensor_addStake',
params,
});
};

public removeStake = (params: RemoveStakeParams) => {
return this.request<SignedTransactionResult>({
id: 42,
jsonrpc: '2.0',
method: 'subtensor_removeStake',
params,
});
};

public moveStake = (params: MoveStakeParams) => {
return this.request<SignedTransactionResult>({
id: 42,
jsonrpc: '2.0',
method: 'subtensor_moveStake',
params,
});
};

public signRaw = (payload: SignRawParams) => {
return this.request<SignedRawResult>({
id: 42,
jsonrpc: '2.0',
method: 'subtensor_signRaw',
params: payload,
});
};
}

export type {
AddProxyParams,
AddStakeParams,
MoveStakeParams,
RemoveStakeParams,
SignRawParams,
SignedRawResult,
SignedTransactionResult,
SubtensorConfig,
TransferParams,
} from './type';
54 changes: 54 additions & 0 deletions packages/@magic-ext/subtensor/src/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
export interface SubtensorConfig {
rpcUrl: string;
}

export interface ConfigType {
rpcUrl: string;
chainType: string;
}

export interface AddProxyParams {
delegate: string;
proxyType?: string;
delay?: number;
}

export interface AddStakeParams {
hotkey: string;
amount: string | bigint;
netuid?: number;
}

export interface RemoveStakeParams {
hotkey: string;
amount: string | bigint;
netuid?: number;
}

export interface MoveStakeParams {
originHotkey: string;
destHotkey: string;
amount: string | bigint;
originNetuid?: number;
destNetuid?: number;
}

export interface TransferParams {
to: string;
amount: string | bigint;
}

export interface SignedTransactionResult {
txHash: string;
}

export interface SignRawParams {
/** Hex-encoded data to sign (e.g. '0x...'). */
data: string;
/** 'bytes' signs raw data; 'payload' treats data as an extrinsic payload. Defaults to 'bytes'. */
type?: 'bytes' | 'payload';
}

export interface SignedRawResult {
signature: string;
}
6 changes: 6 additions & 0 deletions packages/@magic-ext/subtensor/test/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// NOTE: This module is automatically included at the top of each test file.
import { mockConsole } from '../../../../scripts/utils/mock-console';

beforeEach(() => {
mockConsole();
});
87 changes: 87 additions & 0 deletions packages/@magic-ext/subtensor/test/spec/subtensor.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { SubtensorExtension } from '../../src/index';
import { createMagicSDKWithExtension } from '../../../../@magic-sdk/provider/test/factories';

beforeEach(() => {
jest.resetAllMocks();
});

function makeMagic() {
const magic = createMagicSDKWithExtension({}, [
new SubtensorExtension({ rpcUrl: 'wss://entrypoint-finney.opentensor.ai:443' }),
]);
magic.subtensor.request = jest.fn();
return magic;
}

test('extension reports name and chain type', () => {
const ext = new SubtensorExtension({ rpcUrl: 'wss://example' });
expect(ext.name).toBe('subtensor');
});

test('getAccount sends subtensor_getAccount with empty params', () => {
const magic = makeMagic();
magic.subtensor.getAccount();
const payload = (magic.subtensor.request as jest.Mock).mock.calls[0][0];
expect(payload.method).toBe('subtensor_getAccount');
expect(payload.params).toEqual([]);
});

test('sendTransaction forwards { to, amount }', () => {
const magic = makeMagic();
magic.subtensor.sendTransaction({ to: '5Dest', amount: '1000000000' });
const payload = (magic.subtensor.request as jest.Mock).mock.calls[0][0];
expect(payload.method).toBe('subtensor_sendTransaction');
expect(payload.params).toEqual({ to: '5Dest', amount: '1000000000' });
});

test('addProxy forwards delegate + optional proxyType/delay', () => {
const magic = makeMagic();
magic.subtensor.addProxy({ delegate: '5Del', proxyType: 'Staking', delay: 5 });
const payload = (magic.subtensor.request as jest.Mock).mock.calls[0][0];
expect(payload.method).toBe('subtensor_addProxy');
expect(payload.params).toEqual({ delegate: '5Del', proxyType: 'Staking', delay: 5 });
});

test('addStake forwards hotkey, amount, and optional netuid', () => {
const magic = makeMagic();
magic.subtensor.addStake({ hotkey: '5Hot', amount: BigInt(100), netuid: 7 });
const payload = (magic.subtensor.request as jest.Mock).mock.calls[0][0];
expect(payload.method).toBe('subtensor_addStake');
expect(payload.params).toEqual({ hotkey: '5Hot', amount: BigInt(100), netuid: 7 });
});

test('removeStake forwards hotkey + amount', () => {
const magic = makeMagic();
magic.subtensor.removeStake({ hotkey: '5Hot', amount: '50' });
const payload = (magic.subtensor.request as jest.Mock).mock.calls[0][0];
expect(payload.method).toBe('subtensor_removeStake');
expect(payload.params).toEqual({ hotkey: '5Hot', amount: '50' });
});

test('moveStake forwards origin/dest hotkeys and amount', () => {
const magic = makeMagic();
magic.subtensor.moveStake({
originHotkey: '5From',
destHotkey: '5To',
amount: BigInt(200),
originNetuid: 1,
destNetuid: 2,
});
const payload = (magic.subtensor.request as jest.Mock).mock.calls[0][0];
expect(payload.method).toBe('subtensor_moveStake');
expect(payload.params).toEqual({
originHotkey: '5From',
destHotkey: '5To',
amount: BigInt(200),
originNetuid: 1,
destNetuid: 2,
});
});

test('signRaw forwards arbitrary payload as params', () => {
const magic = makeMagic();
magic.subtensor.signRaw({ method: '0xdeadbeef', version: 4 });
const payload = (magic.subtensor.request as jest.Mock).mock.calls[0][0];
expect(payload.method).toBe('subtensor_signRaw');
expect(payload.params).toEqual({ method: '0xdeadbeef', version: 4 });
});
3 changes: 3 additions & 0 deletions packages/@magic-ext/subtensor/test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../../../../tsconfig.settings.test.json",
}
3 changes: 3 additions & 0 deletions packages/@magic-ext/subtensor/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../../../tsconfig.settings.json",
}
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3339,6 +3339,14 @@ __metadata:
languageName: unknown
linkType: soft

"@magic-ext/subtensor@workspace:packages/@magic-ext/subtensor":
version: 0.0.0-use.local
resolution: "@magic-ext/subtensor@workspace:packages/@magic-ext/subtensor"
dependencies:
"@magic-sdk/provider": ^33.7.1
languageName: unknown
linkType: soft

"@magic-ext/sui@workspace:packages/@magic-ext/sui":
version: 0.0.0-use.local
resolution: "@magic-ext/sui@workspace:packages/@magic-ext/sui"
Expand Down
Loading