Skip to content

Commit

Permalink
Add other account types
Browse files Browse the repository at this point in the history
  • Loading branch information
brusherru committed May 23, 2024
1 parent 9045dce commit 6cdcace
Show file tree
Hide file tree
Showing 17 changed files with 402 additions and 167 deletions.
86 changes: 41 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,51 +6,47 @@ See usage examples below:

```js
// Import
import {
TemplateRegistry,
SINGLE_SIG_TEMPLATE_ADDRESS,
SpawnPayload,
hash,
} from '@spacemesh/sm-codec';
import { Stdtemplates, StdPublicKeys, StdMethods } from '@spacemesh/sm-codec';
import ed25519 from '@spacemesh/ed25519-wasm';

// SingleSig example
(async () => {
// Usage
// Get SingleSig template
const singleSig = StdTemplates[StdPublicKeys.SingleSig];
// Get method
const spawnTpl = singleSig.methods[StdMethods.Spawn];

// TemplateRegistry has pre-registered templates.
// You can also register your own templates. See below.

// Single Sig account spawning
const spawnSingleSig = TemplateRegistry.get(
SINGLE_SIG_TEMPLATE_ADDRESS,
0
);
// Prepare SpawnPayload
const spawnPayload: SpawnPayload = {
const spawnPayload = {
Arguments: {
PublicKey: Uint8Array.from([/* your public key: 32 bytes */]),
},
};
// Calculate Principal address (of your new account)
const principal = spawnSingleSig.principal(spawnPayload);
const principal = spawnTpl.principal(spawnPayload);
// Encode SpawnTransaction
const rawTx = spawnSingleSig.encode(principal, spawnPayload);
const rawTx = spawnTpl.encode(principal, spawnPayload);
// Get transaction hash, it is used in signing
const txHash = hash(rawTx);
// Then use `ed25519` library to sign the hash with your private key
const sig = ed25519.sign(myPrivateKey, txHash);
// And finally sign tx (actualy it concatenates bytes)
const signedTx = tpl.sign(rawTx, sig);
const signedTx = spawnTpl.sign(rawTx, sig);

// Note: Principal method exists on any method
// So this example will work as well:
singleSig.methods[StdMethods.Spend].principal(spawnPayload);

})();
```

Example of creating your own template:
```js
import { TemplateRegistry, asTemplate, PublicKey, SingleSig } from '@spacemesh/sm-codec';
import { PublicKey, SingleSig, Codecs } from '@spacemesh/sm-codec';
import { Struct, str } from 'scale-ts';
const spawnCodec = Struct({
Owner: PublicKey,
Nonce: str,
});
const saySmthCodec = Struct({
message: str,
Expand All @@ -62,36 +58,36 @@ import ed25519 from '@spacemesh/ed25519-wasm';
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 100,
]);
// Creating own templates
const myTemplate = {
key: toHex(address),
publicKey: address,
methods: {
0: new Transaction({
address: publicKey,
methodSelector: 0,
spawnArgsCodec: PublicKey,
payloadCodec: spawnCodec,
sigCodec: SingleSig,
}),
1: new Transaction({
address: SINGLE_SIG_TEMPLATE_ADDRESS,
methodSelector: n,
spawnArgsCodec: SpawnArguments,
payloadCodec: saySmthCodec,
sigCodec: SingleSig,
}),
},
const spawnTpl = new Transaction({
address,
methodSelector: 0,
spawnArgsCodec: spawnCodec,
// For Spawn transaction it is neccessary to add a template address into payload
payloadCodec: withTemplateAddress(address, spawnCodec),
sigCodec: SingleSig,
});
// Add it to registry
TemplateRegistry.register(address, myTemplate);
const saySmthTpl =
new Transaction({
address,
methodSelector: 1,
spawnArgsCodec: spawnCodec,
payloadCodec: saySmthCodec,
sigCodec: SingleSig,
});
// Use as in examples above
const principal = saySmthTpl.principal({
Owner: Uint8Array.from([ /* 32 bytes */ ]),
});
// And then use it as described above
const spawnMyAddr = TemplateRegistry.get(address, 0);
const rawTx = saySmthTpl.encode(principal, {
message: 'hello world',
});
})();
```
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"name": "@spacemesh/sm-codec",
"version": "0.5.0",
"version": "0.6.0",
"description": "Spacemesh Transaction Codec library",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"scripts": {
"build": "yarn clean:lib && tsc -p ./tsconfig.build.json",
"test": "jest",
Expand Down
2 changes: 2 additions & 0 deletions src/codecs/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
//
export const ADDRESS_BYTES_LENGTH = 24;
4 changes: 1 addition & 3 deletions src/codecs/core.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Bytes, CodecType } from 'scale-ts';
import { Compact32, Compact64 } from './compact';
//
export const ADDRESS_BYTES_LENGTH = 24;

import { ADDRESS_BYTES_LENGTH } from './constants';
export const Address = Bytes(ADDRESS_BYTES_LENGTH);
export type Address = CodecType<typeof Address>;

Expand Down
4 changes: 4 additions & 0 deletions src/codecs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './compact';
export * from './core';
export * from './signatures';
export { default as withTemplateAddress } from './withTemplateAddress';
39 changes: 29 additions & 10 deletions src/codecs/signatures.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,34 @@
import { Bytes, CodecType, Struct, u8, Vector } from 'scale-ts';
import { Bytes, CodecType, createCodec, Struct, u8 } from 'scale-ts';
import { toBytes } from '../utils/hex';

export const SingleSig = Bytes(64);
export type SingleSig = Uint8Array;

export const MultiSig = (n: number) =>
Struct({
SigCfg: u8,
Signatures: Vector(SingleSig, n),
});
export type MultiSig = CodecType<ReturnType<typeof MultiSig>>;
export const Signatures = createCodec(
(input: Uint8Array[]) => {
const buffer = new Uint8Array(input.length * 64);
input.forEach((sig, i) => {
buffer.set(sig, i * 64);
});
return buffer;
},
(input: Uint8Array | string | ArrayBuffer) => {
const signatures: Uint8Array[] = [];
const inputArray =
input instanceof Uint8Array
? input
: typeof input === 'string'
? toBytes(input)
: new Uint8Array(input);
for (let i = 0; i < inputArray.length; i += 64) {
signatures.push(inputArray.slice(i, i + 64));
}
return signatures;
}
);

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isMultiSigData = (x: any): x is MultiSig =>
x.SigCfg && x.Signatures;
export const MultiSig = Struct({
SigCfg: u8,
Signatures,
});
export type MultiSig = CodecType<typeof MultiSig>;
23 changes: 12 additions & 11 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
export { default as TemplateRegistry } from './registry';
export { default as Transaction } from './transaction';
export { default as hash } from './utils/hash';
export { padBytes, padAddress } from './utils/padBytes';
export * from './template';
export * from './codecs/core';
export * from './codecs/compact';
export * from './codecs/signatures';
export { StdTemplates, StdPublicKeys } from './std';
export { default as SingleSigTemplate } from './std/singlesig';

export * as Codecs from './codecs';
export { default as TemplateRegistry } from './registry';

export * from './std';
export type {
SpawnPayload,
SpendPayload,
TemeplateArgumentsMap,
SpawnTransaction,
SpendTransaction,
} from './std/singlesig';
} from './std';

export { default as SingleSigTemplate } from './std/singlesig';
export { default as MultiSigTemplate } from './std/multisig';
export { default as VaultTemplate } from './std/vault';
export { default as VestingTemplate } from './std/vesting';
26 changes: 4 additions & 22 deletions src/registry.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { StdTemplates } from './std';
import { Template } from './template';
import { bytesToHex, toHex } from './utils/hex';
import { toHex } from './utils/hex';

type DeepWriteable<T> = {
-readonly [P in keyof T]: T[P] extends readonly []
Expand All @@ -12,18 +11,14 @@ type RTemplates = typeof TemplateRegistry.templates;
type RTemplate<A> = A extends keyof RTemplates ? RTemplates[A] : never;
type RMethods<A> = RTemplate<A>['methods'];
type RMethodselectors<A> = keyof RMethods<A>;
// type RMethod<A, MS> = MS extends RMethodselectors<A> ? RMethods<A>[MS] : never;

// Deprecated
// Keeping it for a backward-compatibility
class TemplateRegistry {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static templates: typeof StdTemplates | { [key: string]: Template } = {
static templates = {
...StdTemplates,
};

static register(template: Template) {
const pk = bytesToHex(template.publicKey);
this.templates[pk] = template;
}
static get<A extends keyof RTemplates, MS extends RMethodselectors<A>>(
address: A | Uint8Array,
methodSelector: MS
Expand All @@ -44,19 +39,6 @@ class TemplateRegistry {
const methods = this.templates[key].methods as DeepWriteable<RMethods<A>>;
return methods[methodSelector];
}

static hasTemplate(address: string | Uint8Array) {
const key = toHex(address);
return !!this.templates[key];
}

static hasMethod<
A extends string,
MS extends keyof typeof TemplateRegistry.templates[A]['methods']
>(address: A, methodSelector: MS) {
const key = toHex(address);
return !!this.templates[key].methods[methodSelector];
}
}

export default TemplateRegistry;
9 changes: 9 additions & 0 deletions src/std/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Codec, Struct } from 'scale-ts';
import { GasPrice, Nonce } from '../codecs/core';

export const TxPayload = <T>(Arguments: Codec<T>) =>
Struct({
Nonce,
GasPrice,
Arguments,
});
60 changes: 59 additions & 1 deletion src/std/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,67 @@
import SingleSigTemplate from './singlesig';
import MultiSigTemplate, {
SpawnArguments as MultiSigSpawnArguments,
} from './multisig';
import SingleSigTemplate, {
SpawnArguments as SingleSigSpawnArguments,
SpendArguments,
} from './singlesig';
import VaultTemplate, { SpawnArguments as VaultSpawnArguments } from './vault';
import VestingTemplate, {
SpawnArguments as VestingSpawnArguments,
DrainArguments,
} from './vesting';

export type {
SpawnArguments as SingleSigSpawnArguments,
SpendArguments,
SpawnTransaction,
SpendTransaction,
} from './singlesig';
export type { SpawnArguments as MultiSigSpawnArguments } from './multisig';
export type { SpawnArguments as VaultSpawnArguments } from './vault';
export type {
SpawnArguments as VestingSpawnArguments,
DrainArguments,
} from './vesting';

export const StdTemplates = {
[SingleSigTemplate.key]: SingleSigTemplate,
[MultiSigTemplate.key]: MultiSigTemplate,
[VaultTemplate.key]: VaultTemplate,
[VestingTemplate.key]: VestingTemplate,
};

export const StdPublicKeys = {
SingleSig: SingleSigTemplate.key,
MultiSig: MultiSigTemplate.key,
Vault: VaultTemplate.key,
Vesting: VestingTemplate.key,
};

export const StdMethods = {
Spawn: 0,
Spend: 16,
Drain: 17,
} as const;

export type StdTemplateKeys = keyof typeof StdTemplates;

export type StdMethodSelectors = keyof typeof StdMethods;

export type TemeplateArgumentsMap = {
[SingleSigTemplate.key]: {
[StdMethods.Spawn]: SingleSigSpawnArguments;
[StdMethods.Spend]: SpendArguments;
};
[MultiSigTemplate.key]: {
[StdMethods.Spawn]: MultiSigSpawnArguments;
[StdMethods.Spend]: SpendArguments;
};
[VaultTemplate.key]: {
[StdMethods.Spawn]: VaultSpawnArguments;
};
[VestingTemplate.key]: {
[StdMethods.Spawn]: VestingSpawnArguments;
[StdMethods.Drain]: DrainArguments;
};
};
Loading

0 comments on commit 6cdcace

Please sign in to comment.