diff --git a/packages/iso-filecoin/CHANGELOG.md b/packages/iso-filecoin/CHANGELOG.md deleted file mode 100644 index a3b6544..0000000 --- a/packages/iso-filecoin/CHANGELOG.md +++ /dev/null @@ -1,171 +0,0 @@ -# Changelog - -## [3.0.1](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v3.0.0...iso-filecoin-v3.0.1) (2023-09-21) - - -### Bug Fixes - -* export types ([dfaa4db](https://github.com/hugomrdias/iso-repo/commit/dfaa4dbbb64182c0b6b3b773f3191fc44afd1691)) - -## [3.0.0](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v2.1.0...iso-filecoin-v3.0.0) (2023-09-05) - - -### ⚠ BREAKING CHANGES - -* change rpc methods signature to support fetch options - -### Features - -* change rpc methods signature to support fetch options ([e1908f9](https://github.com/hugomrdias/iso-repo/commit/e1908f9ea05e309c7f1d260ecc18584503155cb4)) - - -### Bug Fixes - -* increase `waitMsg` timeout to 60s ([553a5e1](https://github.com/hugomrdias/iso-repo/commit/553a5e1bc9f7c3a5916c54d453feea6f67ffd414)) -* update deps ([1e0e7ef](https://github.com/hugomrdias/iso-repo/commit/1e0e7ef49e0d48719672129d8aff5c4ddd225ad8)) - -## [2.1.0](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v2.0.2...iso-filecoin-v2.1.0) (2023-07-31) - - -### Features - -* add `fromContractDestination` for address ([#66](https://github.com/hugomrdias/iso-repo/issues/66)) ([d1e6c38](https://github.com/hugomrdias/iso-repo/commit/d1e6c38de792316f24ffd877fd5be557b11d90d8)) - -## [2.0.2](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v2.0.1...iso-filecoin-v2.0.2) (2023-07-25) - - -### Bug Fixes - -* add npm provenance ([ea8a4f3](https://github.com/hugomrdias/iso-repo/commit/ea8a4f3125d0775e92ed03f804344be2be66f05c)) - -## [2.0.1](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v2.0.0...iso-filecoin-v2.0.1) (2023-07-24) - - -### Bug Fixes - -* improve toFormat defaults ([d75c232](https://github.com/hugomrdias/iso-repo/commit/d75c232cfb7a77d06ba46ec14430436ffc20d732)) - -## [2.0.0](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v1.3.0...iso-filecoin-v2.0.0) (2023-07-24) - - -### ⚠ BREAKING CHANGES - -* change token toFormat signature - -### Features - -* change token toFormat signature ([07f50b7](https://github.com/hugomrdias/iso-repo/commit/07f50b7d51530c7368b5206c960edfc36024e34f)) - -## [1.3.0](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v1.2.0...iso-filecoin-v1.3.0) (2023-07-23) - - -### Features - -* add toBigInt and toContractDestination ([88251b7](https://github.com/hugomrdias/iso-repo/commit/88251b70d32f5c35caadfc67f475fc43973ba306)) - -## [1.2.0](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v1.1.1...iso-filecoin-v1.2.0) (2023-07-22) - - -### Features - -* add support for all address protocols ([57ea3ce](https://github.com/hugomrdias/iso-repo/commit/57ea3cefff22130d8a94633fe40e5c3441f2210e)) - -## [1.1.1](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v1.1.0...iso-filecoin-v1.1.1) (2023-07-21) - - -### Bug Fixes - -* add Address.from ([ef89490](https://github.com/hugomrdias/iso-repo/commit/ef89490efc62f82ba43908491e6de2de8c664f1f)) - -## [1.1.0](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v1.0.0...iso-filecoin-v1.1.0) (2023-07-21) - - -### Features - -* add delegated address and eth address ([852a7fb](https://github.com/hugomrdias/iso-repo/commit/852a7fb03483f2fa005107f5849e88ae13d5bef2)) - -## [1.0.0](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v0.2.4...iso-filecoin-v1.0.0) (2023-07-10) - - -### ⚠ BREAKING CHANGES - -* Token `to*` methods return bignumber instead of string and added new toFormat method - -### Features - -* Token `to*` methods return bignumber instead of string and added new toFormat method ([d27f5bc](https://github.com/hugomrdias/iso-repo/commit/d27f5bc8cea879c038e856342ce415fbc078ead9)) - -## [0.2.4](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v0.2.3...iso-filecoin-v0.2.4) (2023-07-07) - - -### Bug Fixes - -* **iso-filecoin:** fix bip39 wordlist import ([168478d](https://github.com/hugomrdias/iso-repo/commit/168478d2de7280bc4847d5d59b257e078d03aacb)) - -## [0.2.3](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v0.2.2...iso-filecoin-v0.2.3) (2023-06-23) - - -### Bug Fixes - -* dont prepare a msg with all the params ([eb32843](https://github.com/hugomrdias/iso-repo/commit/eb32843a8d98a64300a03aa79dffa1c598fe18d6)) - -## [0.2.2](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v0.2.1...iso-filecoin-v0.2.2) (2023-06-22) - - -### Bug Fixes - -* fix token.toBytes and add raw sign ([2611be0](https://github.com/hugomrdias/iso-repo/commit/2611be03a9b92c6f3ad14f98e3b96f0c288d99dc)) - -## [0.2.1](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v0.2.0...iso-filecoin-v0.2.1) (2023-06-22) - - -### Bug Fixes - -* add more entry points to docs ([d31ad2c](https://github.com/hugomrdias/iso-repo/commit/d31ad2c6390daf330052506c81d81c0fd1de0a95)) - -## [0.2.0](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v0.1.5...iso-filecoin-v0.2.0) (2023-06-22) - - -### Features - -* add, msg, sig, rpc ([f574e7b](https://github.com/hugomrdias/iso-repo/commit/f574e7bbba8fcc783f534a669ef156071afc804f)) - -## [0.1.5](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v0.1.3...iso-filecoin-v0.1.5) (2023-05-15) - - -### Miscellaneous Chores - -* **main:** release iso-base 0.1.5 ([3849a49](https://github.com/hugomrdias/iso-repo/commit/3849a49eb867fbdaf3ed95173144b448d4a42f4c)) - -## [0.1.3](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v0.1.2...iso-filecoin-v0.1.3) (2023-05-11) - - -### Bug Fixes - -* update deps ([c5a562f](https://github.com/hugomrdias/iso-repo/commit/c5a562fd8219e99f602e5ac2400bdc0f0dd14336)) - -## [0.1.2](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v0.1.1...iso-filecoin-v0.1.2) (2023-03-28) - - -### Bug Fixes - -* fix docs links ([dca1a62](https://github.com/hugomrdias/iso-repo/commit/dca1a6295155639bb8228cd936837cc86d404345)) - -## [0.1.1](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v0.1.0...iso-filecoin-v0.1.1) (2023-03-28) - - -### Bug Fixes - -* add docs ([f0b2c47](https://github.com/hugomrdias/iso-repo/commit/f0b2c471ea766bb7206fb8add46de76b732ddf1c)) - -## [0.1.0](https://github.com/hugomrdias/iso-repo/compare/iso-filecoin-v0.0.1...iso-filecoin-v0.1.0) (2023-03-28) - - -### Features - -* filecoin package ([a77a184](https://github.com/hugomrdias/iso-repo/commit/a77a1842c01f5e4fa171936b89669c6f10de1cdf)) - - -### Bug Fixes - -* remove unused dep ([191a8b5](https://github.com/hugomrdias/iso-repo/commit/191a8b57d5a246b5053a8b541395bce0e603fa42)) diff --git a/packages/iso-filecoin/package.json b/packages/iso-filecoin/package.json deleted file mode 100644 index df4b4f1..0000000 --- a/packages/iso-filecoin/package.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "name": "iso-filecoin", - "type": "module", - "version": "3.0.1", - "description": "Filecoin Utils", - "author": "Hugo Dias (hugodias.me)", - "license": "MIT", - "homepage": "https://github.com/hugomrdias/iso-repo/tree/main/packages/iso-filecoin", - "repository": { - "url": "hugomrdias/iso-repo", - "directory": "packages/iso-filecoin" - }, - "keywords": [ - "token", - "filecoin", - "crypto", - "wallet" - ], - "exports": { - ".": { - "types": "./dist/src/index.d.ts", - "import": "./src/index.js" - }, - "./wallet": { - "types": "./dist/src/wallet.d.ts", - "import": "./src/wallet.js" - }, - "./token": { - "types": "./dist/src/token.d.ts", - "import": "./src/token.js" - }, - "./address": { - "types": "./dist/src/address.d.ts", - "import": "./src/address.js" - }, - "./rpc": { - "types": "./dist/src/rpc.d.ts", - "import": "./src/rpc.js" - }, - "./signature": { - "types": "./dist/src/signature.d.ts", - "import": "./src/signature.js" - }, - "./message": { - "types": "./dist/src/message.d.ts", - "import": "./src/message.js" - }, - "./utils": { - "types": "./dist/src/utils.d.ts", - "import": "./src/utils.js" - }, - "./types": { - "types": "./dist/src/types.d.ts" - } - }, - "main": "src/index.js", - "types": "dist/src/index.d.ts", - "typesVersions": { - "*": { - "address": [ - "dist/src/address" - ], - "message": [ - "dist/src/message" - ], - "rpc": [ - "dist/src/rpc" - ], - "signature": [ - "dist/src/signature" - ], - "token": [ - "dist/src/token" - ], - "wallet": [ - "dist/src/wallet" - ], - "types": [ - "dist/src/types" - ], - "utils": [ - "dist/src/utils" - ] - } - }, - "files": [ - "src", - "dist/src/*.d.ts", - "dist/src/*.d.ts.map" - ], - "scripts": { - "lint": "tsc --build && biome check --no-errors-on-unmatched --files-ignore-unknown=true .", - "test": "tsc --build && pnpm run test:node && pnpm run test:browser", - "test:node": "mocha 'test/**/!(*.browser).test.js'", - "test:browser": "playwright-test 'test/**/!(*.node).test.js'" - }, - "dependencies": { - "@ipld/dag-cbor": "^9.2.1", - "@noble/curves": "^1.6.0", - "@noble/hashes": "^1.5.0", - "@scure/bip32": "^1.5.0", - "@scure/bip39": "^1.4.0", - "bignumber.js": "^9.1.2", - "iso-base": "workspace:^", - "iso-web": "workspace:^", - "zod": "^3.23.8" - }, - "devDependencies": { - "@types/assert": "^1.5.11", - "@types/mocha": "^10.0.9", - "@types/node": "^22.7.9", - "assert": "^2.1.0", - "mocha": "^10.7.3", - "playwright-test": "^14.1.6", - "typescript": "5.6.3" - }, - "publishConfig": { - "provenance": true - }, - "depcheck": { - "specials": [ - "bin" - ], - "ignores": [ - "@types/*", - "assert" - ] - } -} diff --git a/packages/iso-filecoin/readme.md b/packages/iso-filecoin/readme.md deleted file mode 100644 index 78f85ec..0000000 --- a/packages/iso-filecoin/readme.md +++ /dev/null @@ -1,27 +0,0 @@ -# iso-filecoin [![NPM Version](https://img.shields.io/npm/v/iso-filecoin.svg)](https://www.npmjs.com/package/iso-filecoin) [![License](https://img.shields.io/npm/l/iso-filecoin.svg)](https://github.com/hugomrdias/iso-repo/blob/main/license) [![iso-filecoin](https://github.com/hugomrdias/iso-repo/actions/workflows/iso-filecoin.yml/badge.svg)](https://github.com/hugomrdias/iso-repo/actions/workflows/iso-filecoin.yml) - -Isomorphic filecoin utilities - -> [!IMPORTANT] -> This package was moved to a different organization and repository at . The npm package is still the same `iso-filecoin`. - -## Install - -```bash -pnpm install iso-filecoin -``` - -## Usage - -```js -import { Token } from 'iso-filecoin/token' -import { Wallet } from 'iso-filecoin/wallet' -``` - -## Docs - -Check - -## License - -MIT © [Hugo Dias](http://hugodias.me) diff --git a/packages/iso-filecoin/src/address.js b/packages/iso-filecoin/src/address.js deleted file mode 100644 index b00e8c5..0000000 --- a/packages/iso-filecoin/src/address.js +++ /dev/null @@ -1,652 +0,0 @@ -import { blake2b } from '@noble/hashes/blake2b' -import * as leb128 from 'iso-base/leb128' -import { base16, base32 } from 'iso-base/rfc4648' -import { concat, equals, isBufferSource, u8 } from 'iso-base/utils' -import { NETWORKS, checkNetworkPrefix, getNetwork } from './utils.js' - -/** - * @typedef {import('./types.js').Address} IAddress - * @typedef { string | IAddress | BufferSource} Value - */ - -/** - * Protocol indicator - */ -export const PROTOCOL_INDICATOR = /** @type {const} */ ({ - ID: 0, - SECP256K1: 1, - ACTOR: 2, - BLS: 3, - DELEGATED: 4, -}) - -const symbol = Symbol.for('filecoin-address') - -/** - * Check if object is a {@link IAddress} instance - * - * @param {any} val - * @returns {val is IAddress} - */ -export function isAddress(val) { - return Boolean(val?.[symbol]) -} - -/** - * Validate checksum - * - * @param {Uint8Array} actual - * @param {Uint8Array} expected - */ -function validateChecksum(actual, expected) { - return equals(actual, expected) -} - -/** - * Check if string is valid Ethereum address - * - * @param {string} address - */ -export function isEthAddress(address) { - return /^0x[\dA-Fa-f]{40}$/.test(address) -} - -/** - * Address from Ethereum address - * - * @param {string} address - * @param {import('./types.js').Network} network - * @returns {IAddress} - */ -export function fromEthAddress(address, network) { - return new AddressDelegated( - 10, - base16.decode(address.slice(2).toUpperCase()), - network - ) -} - -/** - * Ethereum address from address - * - * @param {IAddress} address - */ -export function toEthAddress(address) { - if (address.protocol !== PROTOCOL_INDICATOR.DELEGATED) { - throw new Error( - `Invalid protocol indicator: ${address.protocol}. Only Delegated Adresses are supported.` - ) - } - return `0x${base16.encode(address.payload).toLowerCase()}` -} - -/** - * @param {Value} value - Value to convert to address - * @param {import('./types.js').Network} [network] - Network - * @returns {IAddress} - */ -export function from(value, network = 'mainnet') { - if (isBufferSource(value)) { - return fromBytes(u8(value), network) - } - - if (isAddress(value)) { - return value - } - - if (typeof value === 'string') { - return isEthAddress(value) - ? fromEthAddress(value, network) - : fromString(value) - } - - throw new Error(`Invalid value: ${value}`) -} - -/** - * Address from string - * - * @param {string} address - * @returns {IAddress} - */ -export function fromString(address) { - const type = Number.parseInt(address[1]) - - switch (type) { - case PROTOCOL_INDICATOR.SECP256K1: { - return AddressSecp256k1.fromString(address) - } - - case PROTOCOL_INDICATOR.DELEGATED: { - return AddressDelegated.fromString(address) - } - - case PROTOCOL_INDICATOR.ACTOR: { - return AddressActor.fromString(address) - } - - case PROTOCOL_INDICATOR.BLS: { - return AddressBLS.fromString(address) - } - - case PROTOCOL_INDICATOR.ID: { - return AddressId.fromString(address) - } - - default: { - throw new Error(`Invalid protocol indicator: ${type}`) - } - } -} - -/** - * Create address from bytes - * - * @param {Uint8Array} bytes - * @param {import('./types.js').Network} network - * @returns {IAddress} - */ -export function fromBytes(bytes, network) { - const type = bytes[0] - - switch (type) { - case PROTOCOL_INDICATOR.SECP256K1: { - return AddressSecp256k1.fromBytes(bytes, network) - } - case PROTOCOL_INDICATOR.DELEGATED: { - return AddressDelegated.fromBytes(bytes, network) - } - case PROTOCOL_INDICATOR.BLS: { - return AddressBLS.fromBytes(bytes, network) - } - case PROTOCOL_INDICATOR.ACTOR: { - return AddressActor.fromBytes(bytes, network) - } - case PROTOCOL_INDICATOR.ID: { - return AddressId.fromBytes(bytes, network) - } - - default: { - throw new Error(`Invalid protocol indicator: ${type}`) - } - } -} - -/** - * Create address from bytes - * - * @param {Uint8Array} bytes - * @param {import('./types.js').Network} network - * @param {import('./types.js').SignatureType} type - * @returns IAddress - */ -export function fromPublicKey(bytes, network, type) { - switch (type) { - case 'SECP256K1': { - return AddressSecp256k1.fromPublicKey(bytes, network) - } - - case 'BLS': { - return AddressBLS.fromPublicKey(bytes, network) - } - - default: { - throw new Error(`Invalid signature type: ${type}`) - } - } -} - -/** - * Create an `Address` instance from a 0x-prefixed hex string address returned by `Address.toContractDestination()`. - * - * @param {`0x${string}`} address - The 0x-prefixed hex string address returned by `Address.toContractDestination()`. - * @param {import("./types.js").Network} network - The network the address is on. - */ -export function fromContractDestination(address, network) { - if (!address.startsWith('0x')) { - throw new Error(`Expected 0x prefixed hex, instead got: '${address}'`) - } - return fromBytes(base16.decode(address.slice(2)), network) -} - -/** - * Secp256k1 address - * - * @implements {IAddress} - */ -class Address { - /** @type {boolean} */ - [symbol] = true - - /** - * - * @param {Uint8Array} payload - * @param {import("./types.js").Network} network - */ - constructor(payload, network) { - this.payload = payload - this.network = network - this.networkPrefix = NETWORKS[network] - /** @type {import('./types.js').ProtocolIndicatorCode} */ - this.protocol = PROTOCOL_INDICATOR.ID - } - - toString() { - return `${this.networkPrefix}${this.protocol}${base32 - .encode(concat([this.payload, this.checksum()]), false) - .toLowerCase()}` - } - - toBytes() { - return concat([base16.decode(`0${this.protocol}`), this.payload]) - } - - toContractDestination() { - return /** @type {`0x${string}`} */ (`0x${base16.encode(this.toBytes())}`) - } - - checksum() { - return blake2b(this.toBytes(), { - dkLen: 4, - }) - } -} - -/** - * ID Address f0.. - * - * Protocol 0 addresses are simple IDs. All actors have a numeric ID even if they don’t have public keys. The payload of an ID address is base10 encoded. IDs are not hashed and do not have a checksum. - * - * @see https://spec.filecoin.io/appendix/address/#section-appendix.address.protocol-0-ids - * - * @implements {IAddress} - */ -export class AddressId extends Address { - /** - * - * @param {Uint8Array} payload - * @param {import("./types.js").Network} network - */ - constructor(payload, network) { - super(payload, network) - this.protocol = PROTOCOL_INDICATOR.ID - this.id = leb128.unsigned.decode(payload)[0] - } - - /** - * Create address from string - * - * @param {string} address - */ - static fromString(address) { - const networkPrefix = address[0] - const protocolIndicator = address[1] - - if (!checkNetworkPrefix(networkPrefix)) { - throw new Error(`Invalid network: ${networkPrefix}`) - } - - if (Number.parseInt(protocolIndicator) !== PROTOCOL_INDICATOR.ID) { - throw new Error(`Invalid protocol indicator: ${protocolIndicator}`) - } - - const newAddress = new AddressId( - leb128.unsigned.encode(address.slice(2)), - getNetwork(networkPrefix) - ) - - return newAddress - } - - /** - * Create address from bytes - * - * @param {Uint8Array} bytes - * @param {import('./types.js').Network} network - * @returns - */ - static fromBytes(bytes, network) { - if (bytes[0] !== PROTOCOL_INDICATOR.ID) { - throw new Error(`Invalid protocol indicator: ${bytes[0]}`) - } - return new AddressId(bytes.subarray(1), network) - } - - toString() { - return `${this.networkPrefix}${this.protocol}${this.id}` - } -} - -/** - * Secp256k1 address f1.. - * - * @see https://spec.filecoin.io/appendix/address/#section-appendix.address.protocol-1-libsecpk1-elliptic-curve-public-keys - * - * @implements {IAddress} - */ -export class AddressSecp256k1 extends Address { - /** - * - * @param {Uint8Array} payload - * @param {import("./types.js").Network} network - */ - constructor(payload, network) { - super(payload, network) - this.protocol = PROTOCOL_INDICATOR.SECP256K1 - - if (payload.length !== 20) { - throw new Error(`Invalid payload length: ${payload.length} should be 20.`) - } - } - - /** - * Create address from string - * - * @param {string} address - */ - static fromString(address) { - const networkPrefix = address[0] - const protocolIndicator = address[1] - - if (!checkNetworkPrefix(networkPrefix)) { - throw new Error(`Invalid network: ${networkPrefix}`) - } - - if (Number.parseInt(protocolIndicator) !== PROTOCOL_INDICATOR.SECP256K1) { - throw new Error(`Invalid protocol indicator: ${protocolIndicator}`) - } - - const data = base32.decode(address.slice(2).toUpperCase()) - const payload = data.subarray(0, -4) - const checksum = data.subarray(-4) - const newAddress = new AddressSecp256k1(payload, getNetwork(networkPrefix)) - - if (validateChecksum(newAddress.checksum(), checksum)) { - return newAddress - } - throw new Error('Invalid checksum') - } - - /** - * Create address from bytes - * - * @param {Uint8Array} bytes - * @param {import('./types.js').Network} network - * @returns - */ - static fromBytes(bytes, network) { - if (bytes[0] !== PROTOCOL_INDICATOR.SECP256K1) { - throw new Error(`Invalid protocol indicator: ${bytes[0]}`) - } - return new AddressSecp256k1(bytes.subarray(1), network) - } - - /** - * - * @param {Uint8Array} publicKey - * @param {import('./types.js').Network} network - * @returns - */ - static fromPublicKey(publicKey, network) { - if (publicKey.length !== 65) { - throw new Error( - `Invalid public key length: ${publicKey.length} should be 65.` - ) - } - const payload = blake2b(publicKey, { - dkLen: 20, - }) - return new AddressSecp256k1(payload, network) - } -} - -/** - * Actor Address f2.. - * - * Protocol 2 addresses representing an Actor. The payload field contains the SHA256 hash of meaningful data produced as a result of creating the actor. - * - * @see https://spec.filecoin.io/appendix/address/#section-appendix.address.protocol-2-actor - * - * @implements {IAddress} - */ -export class AddressActor extends Address { - /** - * - * @param {Uint8Array} payload - * @param {import("./types.js").Network} network - */ - constructor(payload, network) { - super(payload, network) - this.protocol = PROTOCOL_INDICATOR.ACTOR - if (payload.length !== 20) { - throw new Error(`Invalid payload length: ${payload.length} should be 20.`) - } - } - - /** - * Create address from string - * - * @param {string} address - */ - static fromString(address) { - const networkPrefix = address[0] - const protocolIndicator = address[1] - - if (!checkNetworkPrefix(networkPrefix)) { - throw new Error(`Invalid network: ${networkPrefix}`) - } - - if (Number.parseInt(protocolIndicator) !== PROTOCOL_INDICATOR.ACTOR) { - throw new Error(`Invalid protocol indicator: ${protocolIndicator}`) - } - - const data = base32.decode(address.slice(2).toUpperCase()) - const payload = data.subarray(0, -4) - const checksum = data.subarray(-4) - const newAddress = new AddressActor(payload, getNetwork(networkPrefix)) - - if (validateChecksum(newAddress.checksum(), checksum)) { - return newAddress - } - throw new Error('Invalid checksum') - } - - /** - * Create address from bytes - * - * @param {Uint8Array} bytes - * @param {import('./types.js').Network} network - * @returns - */ - static fromBytes(bytes, network) { - if (bytes[0] !== PROTOCOL_INDICATOR.ACTOR) { - throw new Error(`Invalid protocol indicator: ${bytes[0]}`) - } - return new AddressActor(bytes.subarray(1), network) - } -} - -/** - * BLS Address f3.. - * - * Protocol 3 addresses represent BLS public encryption keys. The payload field contains the BLS public key. - * - * @see https://spec.filecoin.io/appendix/address/#section-appendix.address.protocol-3-bls - * - * @implements {IAddress} - */ -export class AddressBLS extends Address { - /** - * - * @param {Uint8Array} payload - * @param {import("./types.js").Network} network - */ - constructor(payload, network) { - super(payload, network) - this.protocol = PROTOCOL_INDICATOR.BLS - if (payload.length !== 48) { - throw new Error(`Invalid payload length: ${payload.length} should be 48.`) - } - } - - /** - * Create address from string - * - * @param {string} address - */ - static fromString(address) { - const networkPrefix = address[0] - const protocolIndicator = address[1] - - if (!checkNetworkPrefix(networkPrefix)) { - throw new Error(`Invalid network: ${networkPrefix}`) - } - - if (Number.parseInt(protocolIndicator) !== PROTOCOL_INDICATOR.BLS) { - throw new Error( - `Invalid protocol indicator: ${protocolIndicator} expected ${PROTOCOL_INDICATOR.BLS}` - ) - } - - const data = base32.decode(address.slice(2).toUpperCase()) - const payload = data.subarray(0, -4) - const checksum = data.subarray(-4) - const newAddress = new AddressBLS(payload, getNetwork(networkPrefix)) - - if (validateChecksum(newAddress.checksum(), checksum)) { - return newAddress - } - throw new Error('Invalid checksum') - } - - /** - * Create address from bytes - * - * @param {Uint8Array} bytes - * @param {import('./types.js').Network} network - * @returns - */ - static fromBytes(bytes, network) { - if (bytes[0] !== PROTOCOL_INDICATOR.BLS) { - throw new Error(`Invalid protocol indicator: ${bytes[0]}`) - } - return new AddressBLS(bytes.subarray(1), network) - } - - /** - * - * @param {Uint8Array} publicKey - * @param {import('./types.js').Network} network - * @returns - */ - static fromPublicKey(publicKey, network) { - if (publicKey.length !== 48) { - throw new Error( - `Invalid public key length: ${publicKey.length} should be 48.` - ) - } - return new AddressBLS(publicKey, network) - } -} - -/** - * Delegated address f4.. - * - * @see https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0048.md - * - * @example t410f2oekwcmo2pueydmaq53eic2i62crtbeyuzx2gmy - * @implements {IAddress} - */ -export class AddressDelegated extends Address { - /** - * @param {number} namespace - * @param {Uint8Array} payload - * @param {import("./types.js").Network} network - */ - constructor(namespace, payload, network) { - super(payload, network) - this.protocol = PROTOCOL_INDICATOR.DELEGATED - this.namespace = namespace - - if (namespace !== 10) { - throw new Error( - `Invalid namespace: ${namespace}. Only Ethereum Address Manager (EAM) is supported.` - ) - } - if (payload.length === 0 || payload.length > 54) { - throw new Error( - `Invalid payload length: ${payload.length} should be 54 bytes or less.` - ) - } - } - - /** - * Create address from string - * - * @param {string} address - */ - static fromString(address) { - const networkPrefix = address[0] - const protocolIndicator = address[1] - - if (!checkNetworkPrefix(networkPrefix)) { - throw new Error(`Invalid network: ${networkPrefix}`) - } - - if (Number.parseInt(protocolIndicator) !== PROTOCOL_INDICATOR.DELEGATED) { - throw new Error(`Invalid protocol indicator: ${protocolIndicator}`) - } - - // t410f2oekwcmo2pueydmaq53eic2i62crtbeyuzx2gmy - - const namespace = address.slice(2, address.indexOf('f', 2)) - const dataEncoded = address.slice(address.indexOf('f', 2) + 1) - - const data = base32.decode(dataEncoded.toUpperCase()) - const payload = data.subarray(0, -4) - const checksum = data.subarray(-4) - const newAddress = new AddressDelegated( - Number.parseInt(namespace), - payload, - getNetwork(networkPrefix) - ) - - if (validateChecksum(newAddress.checksum(), checksum)) { - return newAddress - } - throw new Error('Invalid checksum') - } - - /** - * Create address from bytes - * - * @param {Uint8Array} bytes - * @param {import('./types.js').Network} network - * @returns - */ - static fromBytes(bytes, network) { - if (bytes[0] !== PROTOCOL_INDICATOR.DELEGATED) { - throw new Error(`Invalid protocol indicator: ${bytes[0]}`) - } - - const [namespace, size] = leb128.unsigned.decode(bytes, 1) - - return new AddressDelegated( - Number(namespace), - bytes.subarray(1 + size), - network - ) - } - - toString() { - return `${this.networkPrefix}${this.protocol}${this.namespace}f${base32 - .encode(concat([this.payload, this.checksum()]), false) - .toLowerCase()}` - } - - toBytes() { - const protocol = leb128.unsigned.encode(this.protocol) - const namespace = leb128.unsigned.encode(this.namespace) - return concat([protocol, namespace, this.payload]) - } -} diff --git a/packages/iso-filecoin/src/index.js b/packages/iso-filecoin/src/index.js deleted file mode 100644 index f29168b..0000000 --- a/packages/iso-filecoin/src/index.js +++ /dev/null @@ -1,7 +0,0 @@ -export * as Address from './address.js' -export * as Message from './message.js' -export * as RPC from './rpc.js' -export * as Signature from './signature.js' -export * as Token from './token.js' -export * as Wallet from './wallet.js' -export * as Utils from './utils.js' diff --git a/packages/iso-filecoin/src/message.js b/packages/iso-filecoin/src/message.js deleted file mode 100644 index f0bfd12..0000000 --- a/packages/iso-filecoin/src/message.js +++ /dev/null @@ -1,143 +0,0 @@ -import { encode } from '@ipld/dag-cbor' -import { base64 } from 'iso-base/rfc4648' -import { z } from 'zod' - -import * as Address from './address.js' -import { Token } from './token.js' - -const MessageSchema = z.object({ - version: z.literal(0).default(0), - to: z.string(), - from: z.string(), - nonce: z.number().nonnegative().safe().default(0), - value: z - .string() - .min(1) - .refine((v) => !v.startsWith('-'), { - message: 'value must not be negative', - }), - gasLimit: z.number().nonnegative().safe().default(0), - gasFeeCap: z.string().default('0'), - gasPremium: z.string().default('0'), - method: z.number().nonnegative().safe().default(0), - params: z.string().default(''), -}) - -const MessageSchemaPartial = MessageSchema.partial({ - version: true, - nonce: true, - gasLimit: true, - gasFeeCap: true, - gasPremium: true, - method: true, - params: true, -}) -export const Schemas = { - message: MessageSchema, - messagePartial: MessageSchemaPartial, -} - -export class Message { - /** - * - * @param {z.infer} msg - */ - constructor(msg) { - const _msg = MessageSchema.parse(msg) - this.version = _msg.version - this.to = _msg.to - this.from = _msg.from - this.nonce = _msg.nonce - this.value = _msg.value - this.gasLimit = _msg.gasLimit - this.gasFeeCap = _msg.gasFeeCap - this.gasPremium = _msg.gasPremium - this.method = _msg.method - this.params = _msg.params - } - - toLotus() { - return { - Version: this.version, - To: this.to, - From: this.from, - Nonce: this.nonce, - Value: this.value, - GasLimit: this.gasLimit, - GasFeeCap: this.gasFeeCap, - GasPremium: this.gasPremium, - Method: this.method, - Params: this.params, - } - } - - /** - * - * @param {import('./types').LotusMessage} json - */ - static fromLotus(json) { - const obj = { - version: json.Version, - to: json.To, - from: json.From, - nonce: json.Nonce, - value: json.Value, - gasLimit: json.GasLimit, - gasFeeCap: json.GasFeeCap, - gasPremium: json.GasPremium, - method: json.Method, - params: json.Params, - } - - return new Message(obj) - } - - /** - * - * @param {import('./rpc.js').RPC} rpc - */ - async prepare(rpc) { - if (this.nonce === 0) { - const nonce = await rpc.nonce(this.from) - if (nonce.error) { - throw new Error(nonce.error.message) - } - - this.nonce = nonce.result - } - - if ( - (this.gasLimit === 0 && this.gasFeeCap === '0') || - this.gasPremium === '0' - ) { - const gas = await rpc.gasEstimate({ msg: this }) - - if (gas.error) { - throw new Error(gas.error.message) - } - - this.gasLimit = gas.result.GasLimit - this.gasFeeCap = gas.result.GasFeeCap - this.gasPremium = gas.result.GasPremium - } - - return this - } - - serialize() { - const msg = [ - this.version, - Address.fromString(this.to).toBytes(), - Address.fromString(this.from).toBytes(), - this.nonce, - Token.fromAttoFIL(this.value).toBytes(), - this.gasLimit, - Token.fromAttoFIL(this.gasFeeCap).toBytes(), - Token.fromAttoFIL(this.gasPremium).toBytes(), - this.method, - base64.decode(this.params), - ] - - return encode(msg) - } -} diff --git a/packages/iso-filecoin/src/rpc.js b/packages/iso-filecoin/src/rpc.js deleted file mode 100644 index d1482ad..0000000 --- a/packages/iso-filecoin/src/rpc.js +++ /dev/null @@ -1,269 +0,0 @@ -import { anySignal } from 'iso-web/signals' -import { Message } from './message.js' -import { Signature } from './signature.js' -import { getNetworkPrefix } from './utils.js' - -export class RPC { - /** - * @param {import("./types.js").Options} options - * @param {import('./types.js').FetchOptions} [fetchOptions] - */ - constructor( - { - api, - token, - network = 'mainnet', - fetch = globalThis.fetch.bind(globalThis), - }, - fetchOptions = {} - ) { - this.fetch = fetch - this.api = new URL(api) - this.network = network - this.headers = { - 'Content-Type': 'application/json', - ...(token ? { Authorization: `Bearer ${token}` } : {}), - } - - this.fetchOptions = fetchOptions - } - - /** - * Version returns the version of the Filecoin node. - * - * @param {import('./types.js').FetchOptions} [fetchOptions] - */ - async version(fetchOptions = {}) { - return /** @type {import('./types.js').VersionResponse} */ ( - await this.call({ method: 'Filecoin.Version' }, fetchOptions) - ) - } - - /** - * NetworkName returns the name of the network the node is synced to. - * - * @param {import('./types.js').FetchOptions} [fetchOptions] - * @returns - */ - async networkName(fetchOptions = {}) { - return /** @type {import("./types.js").StateNetworkNameResponse} */ ( - await this.call({ method: 'Filecoin.StateNetworkName' }, fetchOptions) - ) - } - - /** - * GasEstimateMessageGas estimates gas values for unset message gas fields - * - * @see https://lotus.filecoin.io/reference/lotus/gas/#gasestimatemessagegas - * - * @param {import('./types.js').GasEstimateParams} params - * @param {import('./types.js').FetchOptions} [fetchOptions] - */ - async gasEstimate(params, fetchOptions = {}) { - this.#validateNetwork(params.msg.from) - this.#validateNetwork(params.msg.to) - - return /** @type {import("./types.js").GasEstimateMessageGasResponse} */ ( - await this.call( - { - method: 'Filecoin.GasEstimateMessageGas', - params: [ - new Message(params.msg).toLotus(), - { MaxFee: params.maxFee ?? '0' }, - undefined, - ], - }, - fetchOptions - ) - ) - } - - /** - * WalletBalance returns the balance of the given address at the current head of the chain. - * - * @see https://lotus.filecoin.io/reference/lotus/wallet/#walletbalance - * - * @param {string} address - * @param {import('./types.js').FetchOptions} [fetchOptions] - */ - async balance(address, fetchOptions = {}) { - address = this.#validateNetwork(address) - return /** @type {import("./types.js").WalletBalanceResponse} */ ( - await this.call( - { method: 'Filecoin.WalletBalance', params: [address] }, - fetchOptions - ) - ) - } - - /** - * MpoolGetNonce gets next nonce for the specified sender. Note that this method may not be atomic. Use MpoolPushMessage instead. - * - * @see https://lotus.filecoin.io/reference/lotus/mpool/#mpoolgetnonce - * @param {string} address - * @param {import('./types.js').FetchOptions} [fetchOptions] - */ - async nonce(address, fetchOptions = {}) { - address = this.#validateNetwork(address) - return /** @type {import("./types.js").MpoolGetNonceResponse} */ ( - await this.call( - { method: 'Filecoin.MpoolGetNonce', params: [address] }, - fetchOptions - ) - ) - } - - /** - * MpoolPush pushes a signed message to mempool. - * - * @see https://lotus.filecoin.io/reference/lotus/mpool/#mpoolpush - * - * @param {import('./types.js').PushMessageParams} params - * @param {import('./types.js').FetchOptions} [fetchOptions] - */ - async pushMessage(params, fetchOptions = {}) { - this.#validateNetwork(params.msg.from) - this.#validateNetwork(params.msg.to) - - return /** @type {import("./types.js").MpoolPushResponse} */ ( - await this.call( - { - method: 'Filecoin.MpoolPush', - params: [ - { - Message: new Message(params.msg).toLotus(), - Signature: new Signature(params.signature).toLotus(), - }, - ], - }, - fetchOptions - ) - ) - } - - /** - * StateWaitMsg looks back in the chain for a message. If not found, it blocks until the message arrives on chain, and gets to the indicated confidence depth. - * - * Timeout is increased to 60s instead of the default 5s. - * - * @see https://lotus.filecoin.io/reference/lotus/state/#statewaitmsg - * @param {import('./types.js').waitMsgParams} params - * @param {import('./types.js').FetchOptions} [fetchOptions] - */ - async waitMsg(params, fetchOptions = {}) { - return /** @type {import('./types.js').WaitMsgResponse} */ ( - await this.call( - { - method: 'Filecoin.StateWaitMsg', - params: [ - params.cid, - params.confidence ?? 2, - params.lookback ?? 100, - false, - ], - }, - { timeout: 60_000, ...fetchOptions } - ) - ) - } - - /** - * Generic method to call any method on the lotus rpc api. - * - * @template R - * @param {import('./types.js').RpcOptions} rpcOptions - * @param {import('./types.js').FetchOptions} [fetchOptions] - * @returns {Promise} - */ - - async call(rpcOptions, fetchOptions = {}) { - const opts = { - ...this.fetchOptions, - ...fetchOptions, - } - try { - const res = await this.fetch(this.api, { - method: 'POST', - headers: this.headers, - body: JSON.stringify({ - jsonrpc: '2.0', - method: rpcOptions.method, - params: rpcOptions.params, - id: 1, - }), - signal: anySignal([ - opts.signal, - AbortSignal.timeout(opts.timeout ?? 5000), - ]), - }) - - if (res.ok) { - const json = await res.json() - // eslint-disable-next-line unicorn/prefer-ternary - if (json.result === undefined) { - return /** @type {import("./types.js").RpcError} */ ({ - error: { - code: json.error.code, - message: `RPC_ERROR: ${json.error.message}`, - }, - }) - } - return /** @type {R} */ ({ result: json.result }) - } - const text = await res.text() - let json - try { - json = JSON.parse(text) - } catch { - // ignore error - } - - if (json?.error) { - return /** @type {import("./types.js").RpcError} */ ({ - error: { - code: json.error.code, - message: `RPC_ERROR: ${json.error.message}`, - }, - }) - } - if (!json || !json.error) { - return /** @type {import("./types.js").RpcError} */ ({ - error: { - code: res.status, - message: `HTTP_ERROR: ${res.statusText} ${text ? `- ${text}` : ''}`, - }, - }) - } - } catch (error) { - const err = /** @type {Error} */ (error) - return /** @type {import("./types.js").RpcError} */ ({ - error: { - code: 0, - message: `FETCH_ERROR: ${err.message}`, - }, - }) - } - - return /** @type {import("./types.js").RpcError} */ ({ - error: { - code: 0, - message: 'ERROR: unknown error', - }, - }) - } - - /** - * @param {string} address - */ - #validateNetwork(address) { - const prefix = getNetworkPrefix(this.network) - - if (!address.startsWith(prefix)) { - throw new TypeError( - `Address ${address} does not belong to ${this.network}` - ) - } - - return address - } -} diff --git a/packages/iso-filecoin/src/signature.js b/packages/iso-filecoin/src/signature.js deleted file mode 100644 index 32d3220..0000000 --- a/packages/iso-filecoin/src/signature.js +++ /dev/null @@ -1,75 +0,0 @@ -import { base64 } from 'iso-base/rfc4648' -import { isBufferSource, u8 } from 'iso-base/utils' -import { z } from 'zod' - -export const SIGNATURE_TYPE = /** @type {const} */ ({ - SECP256K1: 1, - BLS: 3, -}) - -export const SIGNATURE_CODE = /** @type {const} */ ({ - 1: 'SECP256K1', - 3: 'BLS', -}) - -/** @type {import("zod").ZodType} */ -const _zBufferSource = z.custom((value) => { - return isBufferSource(value) -}, 'Value must be a BufferSource') - -const zBuf = _zBufferSource.transform((value) => u8(value)) - -/** - * @typedef {z.infer} LotusSignature - */ - -export const Schemas = { - lotusSignature: z.object({ - Type: z.literal(1).or(z.literal(3)), - Data: z.string(), - }), - signature: z.object({ - type: z.enum([SIGNATURE_CODE[1], SIGNATURE_CODE[3]]), - data: zBuf, - }), -} - -export class Signature { - /** - * - * @param {z.infer} sig - */ - constructor(sig) { - sig = Schemas.signature.parse(sig) - this.type = sig.type - this.data = sig.data - } - - get code() { - return SIGNATURE_TYPE[this.type] - } - - /** - * - * @param {z.infer} json - */ - static fromLotus(json) { - json = Schemas.lotusSignature.parse(json) - return new Signature({ - type: SIGNATURE_CODE[json.Type], - data: base64.decode(json.Data), - }) - } - - /** - * Encodes the signature as a JSON object in the Lotus RPC format. - * - * @returns {import("./types.js").LotusSignature} - */ - toLotus() { - return { - Type: this.code, - Data: base64.encode(this.data, true), - } - } -} diff --git a/packages/iso-filecoin/src/token.js b/packages/iso-filecoin/src/token.js deleted file mode 100644 index ab21a64..0000000 --- a/packages/iso-filecoin/src/token.js +++ /dev/null @@ -1,276 +0,0 @@ -import BigNumber from 'bignumber.js' -import { base16 } from 'iso-base/rfc4648' -import { concat } from 'iso-base/utils' - -export const ATTO_DECIMALS = 18 -export const FEMTO_DECIMALS = 15 -export const PICO_DECIMALS = 12 -export const NANO_DECIMALS = 9 -export const MICRO_DECIMALS = 6 -export const MILLI_DECIMALS = 3 -export const WHOLE_DECIMALS = 0 - -const FEMTO_MUL = 10n ** BigInt(MILLI_DECIMALS) -const PICO_MUL = 10n ** BigInt(MICRO_DECIMALS) -const NANO_MUL = 10n ** BigInt(NANO_DECIMALS) -const MICRO_MUL = 10n ** BigInt(PICO_DECIMALS) -const MILLI_MUL = 10n ** BigInt(FEMTO_DECIMALS) -const WHOLE_MUL = 10n ** BigInt(ATTO_DECIMALS) - -const symbol = Symbol.for('filecoin-token') -BigNumber.config({ - EXPONENTIAL_AT: 1e9, - DECIMAL_PLACES: 40, - ALPHABET: '0123456789abcdef', - ROUNDING_MODE: BigNumber.ROUND_HALF_DOWN, -}) - -/** - * @typedef {number | string | BigNumber.Instance | bigint | Token} Value - */ - -/** - * Check if object is a {@link Token} instance - * - * @param {any} val - * @returns {val is Token} - */ -export function isToken(val) { - return Boolean(val?.[symbol]) -} - -/** - * @param {unknown} val - * @returns {val is bigint} - */ -function isBigInt(val) { - return typeof val === 'bigint' -} - -/** - * @param {Value} val - */ -function bn(val) { - if (isBigInt(val)) { - return new BigNumber(val.toString()) - } - - if (isToken(val)) { - return val.val - } - return new BigNumber(val) -} - -/** - * Class to work with different Filecoin denominations. - * - * @see https://docs.filecoin.io/basics/assets/the-fil-token/#denomonations - */ -export class Token { - /** @type {boolean} */ - [symbol] = true - /** - * @param {Value} val - */ - constructor(val) { - /** @type {BigNumber} */ - this.val = bn(val) - } - - /** - * @param {Value} val - */ - static fromAttoFIL(val) { - return new Token(val) - } - - /** - * @param {Value} val - */ - static fromFemtoFIL(val) { - return new Token(val).mul(FEMTO_MUL) - } - - /** - * @param {Value} val - */ - static fromPicoFIL(val) { - return new Token(val).mul(PICO_MUL) - } - - /** - * @param {Value} val - */ - static fromNanoFIL(val) { - return new Token(val).mul(NANO_MUL) - } - - /** - * @param {Value} val - */ - static fromMicroFIL(val) { - return new Token(val).mul(MICRO_MUL) - } - - /** - * @param {Value} val - */ - static fromMilliFIL(val) { - return new Token(val).mul(MILLI_MUL) - } - - /** - * @param {Value} val - */ - static fromFIL(val) { - return new Token(val).mul(WHOLE_MUL) - } - - /** - * @param {Value} val - */ - mul(val) { - return new Token(this.val.times(bn(val))) - } - - /** - * @param {Value} val - */ - div(val) { - return new Token(this.val.div(bn(val))) - } - - abs() { - return new Token(this.val.abs()) - } - - /** - * @param {Value} val - */ - add(val) { - return new Token(this.val.plus(bn(val))) - } - - /** - * @param {Value} val - */ - sub(val) { - return new Token(this.val.minus(bn(val))) - } - - /** - * Serialize the number to a string using the given base. - * - * @param {number | undefined} [base] - */ - toString(base = 10) { - return this.val.toString(base) - } - - /** - * Format the number using the given options. - * - * @param {import('./types.js').FormatOptions} [options] - * @see https://mikemcl.github.io/bignumber.js/#toFor - */ - toFormat(options = {}) { - options = { - prefix: '', - decimalSeparator: '.', - groupSeparator: ',', - groupSize: 3, - secondaryGroupSize: 0, - fractionGroupSeparator: ' ', - fractionGroupSize: 0, - suffix: '', - ...options, - } - const { - decimalPlaces = 18, - roundingMode = BigNumber.ROUND_HALF_DOWN, - ...rest - } = options - return this.val.toFormat(decimalPlaces, roundingMode, rest) - } - - toAttoFIL() { - this.toFormat({ - decimalPlaces: 2, - roundingMode: 1, - }) - return this - } - - toFemtoFIL() { - return this.div(FEMTO_MUL) - } - - toPicoFIL() { - return this.div(PICO_MUL) - } - - toNanoFIL() { - return this.div(NANO_MUL) - } - - toMicroFIL() { - return this.div(MICRO_MUL) - } - - toMilliFIL() { - return this.div(MILLI_MUL) - } - - toFIL() { - return this.div(WHOLE_MUL) - } - - toBigInt() { - return BigInt(this.val.toString()) - } - - toBytes() { - if (this.val.isZero()) { - return new Uint8Array(0) - } - const sign = this.val.isNegative() ? '01' : '00' - - return concat([ - base16.decode(sign), - bigToUint8Array(BigInt(this.val.abs().toString())), - ]) - } -} - -const big0 = BigInt(0) -const big1 = BigInt(1) -const big8 = BigInt(8) - -/** - * - * @see https://jackieli.dev/posts/bigint-to-uint8array/ - * @param {bigint} big - */ -function bigToUint8Array(big) { - // @ts-ignore - if (big < big0) { - const bits = (BigInt(big.toString(2).length) / big8 + big1) * big8 - const prefix1 = big1 << bits - // @ts-ignore - big += prefix1 - } - let hex = big.toString(16) - if (hex.length % 2) { - hex = `0${hex}` - } - const len = hex.length / 2 - const u8 = new Uint8Array(len) - let i = 0 - let j = 0 - while (i < len) { - u8[i] = Number.parseInt(hex.slice(j, j + 2), 16) - i += 1 - j += 2 - } - return u8 -} diff --git a/packages/iso-filecoin/src/types.ts b/packages/iso-filecoin/src/types.ts deleted file mode 100644 index 1137f22..0000000 --- a/packages/iso-filecoin/src/types.ts +++ /dev/null @@ -1,183 +0,0 @@ -import type BigNumber from 'bignumber.js' -import type { z } from 'zod' -import type { PROTOCOL_INDICATOR } from './address' -import type { Schemas as MessageSchemas } from './message' -import type { SIGNATURE_TYPE, Schemas as SignatureSchemas } from './signature' - -export type ProtocolIndicator = typeof PROTOCOL_INDICATOR -export type ProtocolIndicatorCode = ProtocolIndicator[keyof ProtocolIndicator] - -export interface CID { - '/': string -} - -export interface Address { - protocol: ProtocolIndicatorCode - payload: Uint8Array - network: Network - networkPrefix: NetworkPrefix - namespace?: number - id?: bigint - toString: () => string - toBytes: () => Uint8Array - toContractDestination: () => `0x${string}` - checksum: () => Uint8Array -} - -export interface DerivationPathComponents { - purpose: number - coinType: number - account: number - change: number - addressIndex: number -} - -export type Network = 'mainnet' | 'testnet' -export type NetworkPrefix = 'f' | 't' - -// Message types -export type MessageObj = z.infer<(typeof MessageSchemas)['message']> -export type PartialMessageObj = z.infer< - (typeof MessageSchemas)['messagePartial'] -> -export interface LotusMessage { - Version: 0 - To: string - From: string - Nonce: number - Value: string - GasLimit: number - GasFeeCap: string - GasPremium: string - Method: number - Params: string - CID?: { - '/': string - } -} - -// Signature types -export type SignatureType = keyof typeof SIGNATURE_TYPE -export type SignatureCode = (typeof SIGNATURE_TYPE)[SignatureType] - -export type LotusSignature = z.infer< - (typeof SignatureSchemas)['lotusSignature'] -> -export type SignatureObj = z.infer<(typeof SignatureSchemas)['signature']> - -// RPC types -export interface RpcError { - error: { - code: number - message: string - } - result?: undefined -} - -export interface Options { - token?: string - api: string | URL - network?: Network - fetch?: typeof globalThis.fetch -} - -export interface RpcOptions { - method: `Filecoin.${string}` - params?: unknown[] -} - -export interface FetchOptions { - signal?: AbortSignal - keepalive?: boolean - timeout?: number -} - -export interface MsgReceipt { - ExitCode: number - Return: string | null - GasUsed: number - EventsRoot: CID | null -} -export interface MsgLookup { - Height: number - Message: CID - Receipt: MsgReceipt - ReturnDec: unknown | null - TipSet: CID[] -} -/** - * Lotus API responses - * - * @see https://filecoin-shipyard.github.io/js-lotus-client/api/api.html - */ - -export type LotusResponse = { result: T; error: undefined } | RpcError -export type VersionResponse = LotusResponse<{ - Version: string - APIVersion: number - BlockDelay: number -}> -export type StateNetworkNameResponse = LotusResponse -export type MpoolGetNonceResponse = LotusResponse -export type GasEstimateMessageGasResponse = LotusResponse - -/** - * Wallet balance in attoFIL - * - * @example '99999927137190925849' - */ -export type WalletBalanceResponse = LotusResponse -export type MpoolPushResponse = LotusResponse -export type WaitMsgResponse = LotusResponse - -// RPC methods params - -export interface GasEstimateParams { - /** - * Message to estimate gas for - * - * @see https://lotus.filecoin.io/reference/lotus/gas/#gasestimatemessagegas - */ - msg: PartialMessageObj - /** - * Max fee to pay for gas (attoFIL/gas units) - * - * @default '0' - */ - maxFee?: string -} - -export interface PushMessageParams { - msg: MessageObj - signature: SignatureObj -} - -export interface waitMsgParams { - cid: CID - /** - * Confidence depth to wait for - * - * @default 2 - */ - confidence?: number - /** - * How chain epochs to look back to find the message - * - * @default 100 - */ - lookback?: number -} - -// Token types -export type FormatOptions = BigNumber.Format & { - /** - * @default 18 - * @see https://mikemcl.github.io/bignumber.js/#decimal-places - */ - decimalPlaces?: number - /** - * @default BigNumber.ROUND_HALF_DOWN - * @see https://mikemcl.github.io/bignumber.js/#constructor-properties - */ - roundingMode?: BigNumber.RoundingMode -} diff --git a/packages/iso-filecoin/src/utils.js b/packages/iso-filecoin/src/utils.js deleted file mode 100644 index 492bcba..0000000 --- a/packages/iso-filecoin/src/utils.js +++ /dev/null @@ -1,133 +0,0 @@ -/** - * @typedef {import('./types').NetworkPrefix} NetworkPrefix - */ - -/** - * Signature types filecoin network has to sign transactions - */ -export const SIGNATURES = /** @type {const} */ ({ - SECP256K1: 1, - BLS: 3, -}) - -/** - * Filecoin network prefixes - */ -export const NETWORKS = /** @type {const} */ ({ - mainnet: 'f', - testnet: 't', -}) - -/** - * Get network prefix from network - * - * @param {import("./types.js").Network} network - */ -export function getNetworkPrefix(network) { - return network === 'mainnet' ? 'f' : 't' -} - -/** - * Get network from prefix - * - * @param {NetworkPrefix} networkPrefix - * @returns {import('./types').Network} - */ -export function getNetwork(networkPrefix) { - return networkPrefix === 'f' ? 'mainnet' : 'testnet' -} - -/** - * Returns the third position from derivation path - * - * @param {string} path - path to parse - * @returns {import('./types.js').Network} - */ -export function getNetworkFromPath(path) { - const type = parseDerivationPath(path).coinType - if (type === 1) { - return 'testnet' - } - return 'mainnet' -} - -/** - * Checks if the prefix is a valid network prefix - * - * @param {string} prefix - * @returns {prefix is NetworkPrefix} - */ -export function checkNetworkPrefix(prefix) { - return Object.values(NETWORKS).includes(/** @type {NetworkPrefix} */ (prefix)) -} - -export const BIP_32_PATH_REGEX = /^\d+'?$/u - -/** - * Parse a derivation path into its components - * - * @see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#path-levels - * @param {string} path - The derivation path to parse - * @returns {import('./types').DerivationPathComponents} An object containing the derivation path components - */ -export function parseDerivationPath(path) { - const parts = path.split('/') - if (parts.length !== 6) { - throw new Error( - "Invalid derivation path: depth must be 5 \"m / purpose' / coin_type' / account' / change / address_index\"" - ) - } - - if (parts[0] !== 'm') { - throw new Error('Invalid derivation path: depth 0 must be "m"') - } - - if (parts[1] !== "44'") { - throw new Error( - 'Invalid derivation path: The "purpose" node (depth 1) must be the string "44\'"' - ) - } - - if (!BIP_32_PATH_REGEX.test(parts[2]) || !parts[2].endsWith("'")) { - throw new Error( - 'Invalid derivation path: The "coin_type" node (depth 2) must be a hardened BIP-32 node.' - ) - } - - if (!BIP_32_PATH_REGEX.test(parts[3]) || !parts[3].endsWith("'")) { - throw new Error( - 'Invalid derivation path: The "account" node (depth 3) must be a hardened BIP-32 node.' - ) - } - - if (!BIP_32_PATH_REGEX.test(parts[4])) { - throw new Error( - 'Invalid derivation path: The "change" node (depth 4) must be a BIP-32 node.' - ) - } - - if (!BIP_32_PATH_REGEX.test(parts[5])) { - throw new Error( - 'Invalid derivation path: The "address_index" node (depth 5) must be a BIP-32 node.' - ) - } - - const purpose = Number.parseInt(parts[1], 10) - const coinType = Number.parseInt(parts[2], 10) - const account = Number.parseInt(parts[3], 10) - const change = Number.parseInt(parts[4], 10) - const addressIndex = Number.parseInt(parts[5], 10) - if ( - Number.isNaN(purpose) || - Number.isNaN(coinType) || - Number.isNaN(account) || - Number.isNaN(change) || - Number.isNaN(addressIndex) - ) { - throw new TypeError( - 'Invalid derivation path: some of the components cannot be parsed as numbers' - ) - } - - return { purpose, coinType, account, change, addressIndex } -} diff --git a/packages/iso-filecoin/src/wallet.js b/packages/iso-filecoin/src/wallet.js deleted file mode 100644 index 5d38165..0000000 --- a/packages/iso-filecoin/src/wallet.js +++ /dev/null @@ -1,168 +0,0 @@ -import { secp256k1 as secp } from '@noble/curves/secp256k1' -import { blake2b } from '@noble/hashes/blake2b' -import { HDKey } from '@scure/bip32' -import * as bip39 from '@scure/bip39' -import { wordlist } from '@scure/bip39/wordlists/english' -import { concat } from 'iso-base/utils' -import { fromPublicKey } from './address.js' -import { Message } from './message.js' -import { getNetworkFromPath } from './utils.js' - -/** - * - * @returns - */ -export function generateMnemonic() { - return bip39.generateMnemonic(wordlist, 256) -} - -/** - * @param {string} mnemonic - * @param {string} [password] - */ -export function mnemonicToSeed(mnemonic, password) { - return bip39.mnemonicToSeedSync(mnemonic, password) -} - -/** - * @param {string} mnemonic - * @param {import('./types.js').SignatureType} type - * @param {string} path - * @param {string} [password] - * @param {import('./types.js').Network} [network] - */ -export function accountFromMnemonic(mnemonic, type, path, password, network) { - const seed = mnemonicToSeed(mnemonic, password) - return accountFromSeed(seed, type, path, network) -} - -/** - * - * @param {Uint8Array} seed - * @param {import('./types.js').SignatureType} type - * @param {string} path - * @param {import('./types.js').Network} [network] - */ -export function accountFromSeed(seed, type, path, network) { - switch (type) { - case 'SECP256K1': { - const masterKey = HDKey.fromMasterSeed(seed) - const privateKey = masterKey.derive(path).privateKey - - if (!privateKey) { - throw new Error('Private key could not be generated.') - } - - if (!network) { - network = getNetworkFromPath(path) - } - - const { address, pubKey } = getPublicKey(privateKey, network) - - return { - type, - privateKey, - pubKey, - address, - path, - } - } - - default: { - throw new Error('Not supported.') - } - } -} - -/** - * Account from private key - * - * @param {Uint8Array} privateKey - * @param {import('./types.js').SignatureType} type - * @param {import('./types.js').Network} network - * @param {string} [path] - */ -export function accountFromPrivateKey(privateKey, type, network, path) { - switch (type) { - case 'SECP256K1': { - if (privateKey.length !== 32) { - throw new Error('Private key should be 32 bytes.') - } - - const { address, pubKey } = getPublicKey(privateKey, network) - - return { - type, - privateKey, - pubKey, - address, - path, - } - } - - default: { - throw new Error('Not supported.') - } - } -} - -/** - * @param {Uint8Array} privateKey - * @param {import('./types.js').Network} network - */ -export function getPublicKey(privateKey, network) { - const publicKey = secp.getPublicKey(privateKey, false) - - return { - pubKey: publicKey, - address: fromPublicKey(publicKey, network, 'SECP256K1'), - } -} - -/** - * Sign filecoin message - * - * @param {Uint8Array} privateKey - * @param {import('./types.js').SignatureType} type - * @param {import('./types.js').MessageObj} message - * @returns - */ -export function signMessage(privateKey, type, message) { - const msg = new Message(message).serialize() - return sign(privateKey, type, msg) -} - -/** - * Sign message - * - * @param {Uint8Array} privateKey - * @param {import('./types.js').SignatureType} type - * @param {string | Uint8Array} message - * @returns - */ -export function sign(privateKey, type, message) { - const cid = concat([ - Uint8Array.from([0x01, 0x71, 0xa0, 0xe4, 0x02, 0x20]), - blake2b(message, { - dkLen: 32, - }), - ]) - - const digest = blake2b(cid, { - dkLen: 32, - }) - - switch (type) { - case 'SECP256K1': { - const signature = secp.sign(digest, privateKey) - return concat([ - signature.toCompactRawBytes(), - // @ts-ignore - Uint8Array.from([signature.recovery]), - ]) - } - default: { - throw new Error('Not supported.') - } - } -} diff --git a/packages/iso-filecoin/test/address.test.js b/packages/iso-filecoin/test/address.test.js deleted file mode 100644 index 77b88d7..0000000 --- a/packages/iso-filecoin/test/address.test.js +++ /dev/null @@ -1,292 +0,0 @@ -import assert from 'assert' -import { base16, base64pad } from 'iso-base/rfc4648' -import { - from, - fromBytes, - fromContractDestination, - fromEthAddress, - fromPublicKey, - fromString, - isAddress, - isEthAddress, - toEthAddress, -} from '../src/address.js' - -const secp = [ - [ - 'f17uoq6tp427uzv7fztkbsnn64iwotfrristwpryy', - '01fd1d0f4dfcd7e99afcb99a8326b7dc459d32c628', - ], - [ - 'f1xcbgdhkgkwht3hrrnui3jdopeejsoatkzmoltqy', - '01b882619d46558f3d9e316d11b48dcf211327026a', - ], - [ - 'f1xtwapqc6nh4si2hcwpr3656iotzmlwumogqbuaa', - '01bcec07c05e69f92468e2b3e3bf77c874f2c5da8c', - ], - [ - 'f1wbxhu3ypkuo6eyp6hjx6davuelxaxrvwb2kuwva', - '01b06e7a6f0f551de261fe3a6fe182b422ee0bc6b6', - ], - [ - 'f12fiakbhe2gwd5cnmrenekasyn6v5tnaxaqizq6a', - '01d1500504e4d1ac3e89ac891a4502586fabd9b417', - ], -] - -const actor = [ - [ - 'f24vg6ut43yw2h2jqydgbg2xq7x6f4kub3bg6as6i', - '02e54dea4f9bc5b47d261819826d5e1fbf8bc5503b', - ], - [ - 'f25nml2cfbljvn4goqtclhifepvfnicv6g7mfmmvq', - '02eb58bd08a15a6ade19d0989674148fa95a8157c6', - ], - [ - 'f2nuqrg7vuysaue2pistjjnt3fadsdzvyuatqtfei', - '026d21137eb4c4814269e894d296cf6500e43cd714', - ], - [ - 'f24dd4ox4c2vpf5vk5wkadgyyn6qtuvgcpxxon64a', - '02e0c7c75f82d55e5ed55db28033630df4274a984f', - ], - [ - 'f2gfvuyh7v2sx3patm5k23wdzmhyhtmqctasbr23y', - '02316b4c1ff5d4afb7826ceab5bb0f2c3e0f364053', - ], -] - -const bls = [ - [ - 'f3vvmn62lofvhjd2ugzca6sof2j2ubwok6cj4xxbfzz4yuxfkgobpihhd2thlanmsh3w2ptld2gqkn2jvlss4a', - '03ad58df696e2d4e91ea86c881e938ba4ea81b395e12797b84b9cf314b9546705e839c7a99d606b247ddb4f9ac7a3414dd', - ], - [ - 'f3wmuu6crofhqmm3v4enos73okk2l366ck6yc4owxwbdtkmpk42ohkqxfitcpa57pjdcftql4tojda2poeruwa', - '03b3294f0a2e29e0c66ebc235d2fedca5697bf784af605c75af608e6a63d5cd38ea85ca8989e0efde9188b382f9372460d', - ], - [ - 'f3s2q2hzhkpiknjgmf4zq3ejab2rh62qbndueslmsdzervrhapxr7dftie4kpnpdiv2n6tvkr743ndhrsw6d3a', - '0396a1a3e4ea7a14d49985e661b22401d44fed402d1d0925b243c923589c0fbc7e32cd04e29ed78d15d37d3aaa3fe6da33', - ], -] - -const delegated = [ - [ - 'f410feot7hrogmplrcupubsdbbqarkdewmb4vkwc5qqq', - '040a23a7f3c5c663d71151f40c8610c01150c9660795', - ], - [ - 'f410fek3n2tlnqghc5phd2lqatisj7d57j2lf5hgcs2q', - '040a22b6dd4d6d818e2ebce3d2e009a249f8fbf4e965', - ], - [ - 'f410firjm446mw5tqabmlxjp4sxwshdhrff7adk4srea', - '040a4452ce73ccb76700058bba5fc95ed238cf1297e0', - ], -] - -const id = [ - ['f00', '0000'], - ['f0150', '009601'], - ['f01024', '008008'], - ['f01729', '00c10d'], - ['f018446744073709551615', '00ffffffffffffffffff01'], -] - -describe('address', () => { - for (const [address, expected] of id) { - it(`id vectors ${address} fromString`, () => { - const a = fromString(address) - assert.ok(isAddress(a)) - assert.equal(a.protocol, 0) - assert.strictEqual(base16.encode(a.toBytes()).toLowerCase(), expected) - }) - - it(`id vectors ${address} fromBytes`, () => { - const a = fromBytes(base16.decode(expected.toUpperCase()), 'mainnet') - - assert.strictEqual(a.toString(), address) - }) - - it(`id vectors ${address} from`, () => { - const a = from(base16.decode(expected.toUpperCase()), 'mainnet') - assert.strictEqual(a.toString(), address) - assert.ok(isAddress(a)) - - const b = from(address) - assert.equal(a.protocol, 0) - assert.strictEqual(base16.encode(b.toBytes()).toLowerCase(), expected) - }) - } - - for (const [address, expected] of bls) { - it(`bls vectors ${address} fromString`, () => { - const a = fromString(address) - assert.ok(isAddress(a)) - assert.equal(a.protocol, 3) - assert.strictEqual(base16.encode(a.toBytes()).toLowerCase(), expected) - }) - - it(`bls vectors ${address} fromBytes`, () => { - const a = fromBytes(base16.decode(expected.toUpperCase()), 'mainnet') - - assert.strictEqual(a.toString(), address) - }) - - it(`bls vectors ${address} from`, () => { - const a = from(base16.decode(expected.toUpperCase()), 'mainnet') - assert.strictEqual(a.toString(), address) - assert.ok(isAddress(a)) - - const b = from(address) - assert.equal(a.protocol, 3) - assert.strictEqual(base16.encode(b.toBytes()).toLowerCase(), expected) - }) - } - - for (const [address, expected] of actor) { - it(`actor vectors ${address} fromString`, () => { - const a = fromString(address) - assert.ok(isAddress(a)) - assert.equal(a.protocol, 2) - assert.strictEqual(base16.encode(a.toBytes()).toLowerCase(), expected) - }) - - it(`actor vectors ${address} fromBytes`, () => { - const a = fromBytes(base16.decode(expected.toUpperCase()), 'mainnet') - - assert.strictEqual(a.toString(), address) - }) - - it(`actor vectors ${address} from`, () => { - const a = from(base16.decode(expected.toUpperCase()), 'mainnet') - assert.strictEqual(a.toString(), address) - assert.ok(isAddress(a)) - - const b = from(address) - assert.equal(a.protocol, 2) - assert.strictEqual(base16.encode(b.toBytes()).toLowerCase(), expected) - }) - } - - for (const [address, expected] of secp) { - it(`sepc256k1 vectors ${address} fromString`, () => { - const a = fromString(address) - assert.ok(isAddress(a)) - assert.equal(a.protocol, 1) - assert.strictEqual(base16.encode(a.toBytes()).toLowerCase(), expected) - }) - - it(`sepc256k1 vectors ${address} fromBytes`, () => { - const a = fromBytes(base16.decode(expected.toUpperCase()), 'mainnet') - - assert.strictEqual(a.toString(), address) - }) - - it(`sepc256k1 vectors ${address} from`, () => { - const a = from(base16.decode(expected.toUpperCase()), 'mainnet') - assert.strictEqual(a.toString(), address) - assert.ok(isAddress(a)) - - const b = from(address) - assert.equal(a.protocol, 1) - assert.strictEqual(base16.encode(b.toBytes()).toLowerCase(), expected) - }) - } - - it('from public key', () => { - const a = fromPublicKey( - base64pad.decode( - 'BIgvf8Me7SAb7jpWOH2B5PwmhiaCz1R5BZKraZ2heQuFPk3DACNkUwP3ffqTYcE7Y1SjZeqrF8J0uraaYQjBtSs=' - ), - 'mainnet', - 'SECP256K1' - ) - - assert.strictEqual( - a.toString(), - 'f1eyo4qsoe7kjpehccfhhxlvd6wfo6mbi3ikfiheq' - ) - }) - - for (const [address, expected] of delegated) { - it(`delegated vectors ${address}`, () => { - const a = fromString(address) - - assert.ok(isAddress(a)) - assert.equal(a.protocol, 4) - assert.strictEqual(base16.encode(a.toBytes()).toLowerCase(), expected) - }) - - it(`delegated vectors ${address} fromBytes`, () => { - const a = fromBytes(base16.decode(expected.toUpperCase()), 'mainnet') - - assert.strictEqual(a.toString(), address) - }) - } - - it('is eth address', () => { - assert.ok(isEthAddress('0xd388ab098ed3e84c0d808776440b48f685198498')) - }) - - it('should convert from eth address', () => { - const f4 = fromEthAddress( - '0xd388ab098ed3e84c0d808776440b48f685198498', - 'testnet' - ) - - assert.strictEqual( - f4.toString(), - 't410f2oekwcmo2pueydmaq53eic2i62crtbeyuzx2gmy' - ) - }) - - it('should convert from eth address with "from" ', () => { - const f4 = from('0xd388ab098ed3e84c0d808776440b48f685198498', 'testnet') - - assert.strictEqual( - f4.toString(), - 't410f2oekwcmo2pueydmaq53eic2i62crtbeyuzx2gmy' - ) - }) - - it('should convert from f4 to eth address', () => { - const f4 = fromString('f410f2oekwcmo2pueydmaq53eic2i62crtbeyuzx2gmy') - - assert.strictEqual( - toEthAddress(f4), - '0xd388ab098ed3e84c0d808776440b48f685198498' - ) - }) - - it('should fail convert from f1 to eth address', () => { - const f1 = fromString('f1wbxhu3ypkuo6eyp6hjx6davuelxaxrvwb2kuwva') - - assert.throws(() => toEthAddress(f1)) - }) - - it('should convert from f1 address to contract destination and back', () => { - const f1 = fromString('f1wbxhu3ypkuo6eyp6hjx6davuelxaxrvwb2kuwva') - const contractDestination = f1.toContractDestination() - const f1FromContractDestination = fromContractDestination( - contractDestination, - 'mainnet' - ) - - assert.strictEqual(f1.toString(), f1FromContractDestination.toString()) - }) - - it('should convert from f1 address to contract destination and back on testnet', () => { - const t1 = fromString('t1wbxhu3ypkuo6eyp6hjx6davuelxaxrvwb2kuwva') - const contractDestination = t1.toContractDestination() - const t1FromContractDestination = fromContractDestination( - contractDestination, - 'testnet' - ) - - assert.strictEqual(t1.toString(), t1FromContractDestination.toString()) - }) -}) diff --git a/packages/iso-filecoin/test/message.test.js b/packages/iso-filecoin/test/message.test.js deleted file mode 100644 index ecd622e..0000000 --- a/packages/iso-filecoin/test/message.test.js +++ /dev/null @@ -1,44 +0,0 @@ -import assert from 'assert' -import { Schemas } from '../src/message.js' - -describe('message validation', () => { - it('should validate', () => { - const valid = Schemas.message.safeParse({ - version: 0, - to: 't1pc2apytmdas3sn5ylwhfa32jfpx7ez7ykieelna', - from: 't1pc2apytmdas3sn5ylwhfa32jfpx7ez7ykieelna', - nonce: 0, - value: '1000000000000000000', - gasLimit: 1_000_000, - gasFeeCap: '1000000000000000000', - gasPremium: '1000000000000000000', - method: 0, - params: '', - }) - - assert.ok(valid.success) - }) - - it('should validate from partial', () => { - const valid = Schemas.message.safeParse({ - to: 't1pc2apytmdas3sn5ylwhfa32jfpx7ez7ykieelna', - from: 't1pc2apytmdas3sn5ylwhfa32jfpx7ez7ykieelna', - value: '1000000000000000000', - }) - - assert.ok(valid.success) - - assert.deepEqual(valid.data, { - version: 0, - to: 't1pc2apytmdas3sn5ylwhfa32jfpx7ez7ykieelna', - from: 't1pc2apytmdas3sn5ylwhfa32jfpx7ez7ykieelna', - nonce: 0, - value: '1000000000000000000', - gasLimit: 0, - gasFeeCap: '0', - gasPremium: '0', - method: 0, - params: '', - }) - }) -}) diff --git a/packages/iso-filecoin/test/rpc.test.js b/packages/iso-filecoin/test/rpc.test.js deleted file mode 100644 index b44fd3b..0000000 --- a/packages/iso-filecoin/test/rpc.test.js +++ /dev/null @@ -1,283 +0,0 @@ -import assert from 'assert' -import { Address } from '../src/index.js' -import { Message } from '../src/message.js' -import { RPC } from '../src/rpc.js' -import * as Wallet from '../src/wallet.js' - -const API = 'https://api.calibration.node.glif.io' - -describe('lotus rpc', function () { - this.retries(3) - this.timeout(10_000) - it('version', async () => { - const rpc = new RPC({ api: API }) - - const version = await rpc.version() - if (version.error) { - return assert.fail(version.error.message) - } - - assert(version.result.Version) - assert(version.result.APIVersion) - assert(version.result.BlockDelay) - }) - - it('networkName', async () => { - const rpc = new RPC({ api: API }) - - const version = await rpc.networkName() - if (version.error) { - return assert.fail(version.error.message) - } - - assert.equal(version.result, 'calibrationnet') - }) - - it('nonce', async () => { - const rpc = new RPC({ api: API, network: 'testnet' }) - - const version = await rpc.nonce('t1pc2apytmdas3sn5ylwhfa32jfpx7ez7ykieelna') - if (version.error) { - return assert.fail(version.error.message) - } - - assert.ok(Number.isInteger(version.result)) - }) - - it('nonce fail with wrong network', async () => { - const rpc = new RPC({ api: API, network: 'testnet' }) - - await assert.rejects(() => - rpc.nonce('f1jbnosztqwadgh4smvsnojdvwjgqxsmtzy5n5imi') - ) - }) - - it('gas estimate', async () => { - const rpc = new RPC({ api: API, network: 'testnet' }) - - const nonce = await rpc.nonce('t1pc2apytmdas3sn5ylwhfa32jfpx7ez7ykieelna') - if (nonce.error) { - return assert.fail(nonce.error.message) - } - - const msg = new Message({ - from: 't1pc2apytmdas3sn5ylwhfa32jfpx7ez7ykieelna', - to: 't1sfizuhpgjqyl4yjydlebncvecf3q2cmeeathzwi', - nonce: nonce.result, - value: '100000000000000000', - }) - - const estimate = await rpc.gasEstimate({ msg }) - if (estimate.error) { - return assert.fail(estimate.error.message) - } - - assert.equal(Message.fromLotus(estimate.result).value, msg.value) - }) - - it('gas estimate to delegated address', async () => { - const rpc = new RPC({ api: API, network: 'testnet' }) - - const nonce = await rpc.nonce('t1pc2apytmdas3sn5ylwhfa32jfpx7ez7ykieelna') - if (nonce.error) { - return assert.fail(nonce.error.message) - } - - const msg = new Message({ - from: 't1pc2apytmdas3sn5ylwhfa32jfpx7ez7ykieelna', - to: 't410fpzfl2y5hzayuzqunhcbqgrzdkpmij4uswh5uumi', - nonce: nonce.result, - value: '100000000000000000', - }) - - const estimate = await rpc.gasEstimate({ msg }) - if (estimate.error) { - return assert.fail(estimate.error.message) - } - - assert.equal(Message.fromLotus(estimate.result).value, msg.value) - }) - - it('gas estimate to 0x address', async () => { - const rpc = new RPC({ api: API, network: 'testnet' }) - - const nonce = await rpc.nonce('t1pc2apytmdas3sn5ylwhfa32jfpx7ez7ykieelna') - if (nonce.error) { - return assert.fail(nonce.error.message) - } - - const msg = new Message({ - from: 't1pc2apytmdas3sn5ylwhfa32jfpx7ez7ykieelna', - to: Address.from( - '0x7e4ABd63A7C8314Cc28D388303472353D884f292', - 'testnet' - ).toString(), - nonce: nonce.result, - value: '100000000000000000', - }) - - const estimate = await rpc.gasEstimate({ msg }) - if (estimate.error) { - return assert.fail(estimate.error.message) - } - - assert.equal(Message.fromLotus(estimate.result).value, msg.value) - }) - - it('balance', async () => { - const rpc = new RPC({ api: API, network: 'testnet' }) - - const balance = await rpc.balance( - 't1jzly7yqqff5fignjddktuo2con2pjoz5yajemli' - ) - if (balance.error) { - return assert.fail(balance.error.message) - } - - assert.ok(typeof balance.result === 'string') - }) - - it('balance from delegated address', async () => { - const rpc = new RPC({ api: API, network: 'testnet' }) - - const balance = await rpc.balance( - 't410fpzfl2y5hzayuzqunhcbqgrzdkpmij4uswh5uumi' - ) - if (balance.error) { - return assert.fail(balance.error.message) - } - - assert.ok(typeof balance.result === 'string') - assert.ok(BigInt(balance.result) > 0n) - }) - - it('balance from 0x address', async () => { - const rpc = new RPC({ api: API, network: 'testnet' }) - - const balance = await rpc.balance( - Address.from( - '0x7e4ABd63A7C8314Cc28D388303472353D884f292', - 'testnet' - ).toString() - ) - if (balance.error) { - return assert.fail(balance.error.message) - } - - assert.ok(typeof balance.result === 'string') - assert.ok(BigInt(balance.result) > 0n) - }) - - it('send message', async () => { - const rpc = new RPC({ api: API, network: 'testnet' }) - const account = Wallet.accountFromMnemonic( - 'already turtle birth enroll since owner keep patch skirt drift any dinner', - 'SECP256K1', - "m/44'/1'/0'/0/0", - 'testnet' - ) - const message = await new Message({ - to: 't1sfizuhpgjqyl4yjydlebncvecf3q2cmeeathzwi', - from: account.address.toString(), - value: '1', - }).prepare(rpc) - - const balance = await rpc.pushMessage({ - msg: message, - signature: { - type: 'SECP256K1', - data: Wallet.signMessage(account.privateKey, 'SECP256K1', message), - }, - }) - if (balance.error) { - return assert.fail(balance.error.message) - } - - assert.ok(typeof balance.result['/'] === 'string') - }) - - it('send message to 0x', async () => { - const rpc = new RPC({ api: API, network: 'testnet' }) - const account = Wallet.accountFromMnemonic( - 'already turtle birth enroll since owner keep patch skirt drift any dinner', - 'SECP256K1', - "m/44'/1'/0'/0/0", - 'testnet' - ) - const message = await new Message({ - to: Address.from( - '0x7e4ABd63A7C8314Cc28D388303472353D884f292', - 'testnet' - ).toString(), - from: account.address.toString(), - value: '1', - }).prepare(rpc) - - const balance = await rpc.pushMessage({ - msg: message, - signature: { - type: 'SECP256K1', - data: Wallet.signMessage(account.privateKey, 'SECP256K1', message), - }, - }) - if (balance.error) { - return assert.fail(balance.error.message) - } - - assert.ok(typeof balance.result['/'] === 'string') - }) -}) - -describe('lotus rpc aborts', function () { - this.retries(3) - this.timeout(10_000) - it('timeout', async () => { - const rpc = new RPC({ api: API }, { timeout: 100 }) - - const version = await rpc.version() - - assert.ok(version.error) - - assert.ok(version.error.message.includes('FETCH_ERROR')) - }) - - it('timeout on method', async () => { - const rpc = new RPC({ api: API }, { timeout: 100 }) - - const version = await rpc.version({ timeout: 10 }) - - assert.ok(version.error) - assert.ok(version.error.message.includes('FETCH_ERROR')) - }) - - it('timeout default', async () => { - const rpc = new RPC({ api: API }) - - const version = await rpc.version() - - assert.ok(version.result) - }) - - it('aborted', async () => { - const rpc = new RPC({ api: API }, { signal: AbortSignal.abort() }) - - const version = await rpc.version() - - assert.ok(version.error) - - assert.ok(version.error.message.includes('FETCH_ERROR')) - }) - - it('abort', async () => { - const controller = new AbortController() - const rpc = new RPC({ api: API }) - - const version = rpc.version({ signal: controller.signal }) - controller.abort() - - const rsp = await version - assert.ok(rsp.error) - - assert.ok(rsp.error.message.includes('FETCH_ERROR')) - }) -}) diff --git a/packages/iso-filecoin/test/token.test.js b/packages/iso-filecoin/test/token.test.js deleted file mode 100644 index f3811c9..0000000 --- a/packages/iso-filecoin/test/token.test.js +++ /dev/null @@ -1,149 +0,0 @@ -import assert from 'assert' -import { base16 } from 'iso-base/rfc4648' -import { Token } from '../src/token.js' - -describe('token', () => { - it('zero', () => { - assert.strictEqual(new Token(0).toAttoFIL().toString(), '0') - assert.strictEqual(new Token(0).toFemtoFIL().toString(), '0') - assert.strictEqual(new Token(0).toPicoFIL().toString(), '0') - assert.strictEqual(new Token(0).toNanoFIL().toString(), '0') - assert.strictEqual(new Token(0).toMicroFIL().toString(), '0') - assert.strictEqual(new Token(0).toMilliFIL().toString(), '0') - assert.strictEqual(new Token(0).toFIL().toString(), '0') - }) - - it('positive', () => { - assert.strictEqual( - Token.fromFIL(1).toAttoFIL().toString(), - '1000000000000000000' - ) - assert.strictEqual( - Token.fromFIL(1).toFemtoFIL().toString(), - '1000000000000000' - ) - assert.strictEqual(Token.fromFIL(1).toPicoFIL().toString(), '1000000000000') - assert.strictEqual(Token.fromFIL(1).toNanoFIL().toString(), '1000000000') - assert.strictEqual(Token.fromFIL(1).toMicroFIL().toString(), '1000000') - assert.strictEqual(Token.fromFIL(1).toMilliFIL().toString(), '1000') - assert.strictEqual(Token.fromFIL(1).toFIL().toString(), '1') - }) - - it('negative', () => { - assert.strictEqual( - Token.fromFIL(-1).toAttoFIL().toString(), - '-1000000000000000000' - ) - assert.strictEqual( - Token.fromFIL(-1).toFemtoFIL().toString(), - '-1000000000000000' - ) - assert.strictEqual( - Token.fromFIL(-1).toPicoFIL().toString(), - '-1000000000000' - ) - assert.strictEqual(Token.fromFIL(-1).toNanoFIL().toString(), '-1000000000') - assert.strictEqual(Token.fromFIL(-1).toMicroFIL().toString(), '-1000000') - assert.strictEqual(Token.fromFIL(-1).toMilliFIL().toString(), '-1000') - assert.strictEqual(Token.fromFIL(-1).toFIL().toString(), '-1') - }) - - it('float', () => { - assert.strictEqual( - Token.fromFIL(0.001).toAttoFIL().toString(), - '1000000000000000' - ) - assert.strictEqual( - Token.fromFIL(0.001).toFemtoFIL().toString(), - '1000000000000' - ) - assert.strictEqual( - Token.fromFIL(0.001).toPicoFIL().toString(), - '1000000000' - ) - assert.strictEqual(Token.fromFIL(0.001).toNanoFIL().toString(), '1000000') - assert.strictEqual(Token.fromFIL(0.001).toMicroFIL().toString(), '1000') - assert.strictEqual(Token.fromFIL(0.001).toMilliFIL().toString(), '1') - assert.strictEqual(Token.fromFIL(0.001).toFIL().toString(), '0.001') - }) - - it('precision bigint', () => { - assert.strictEqual( - Token.fromAttoFIL(11_231_000_001_100_000_000_011n).toAttoFIL().toString(), - '11231000001100000000011' - ) - assert.strictEqual( - Token.fromAttoFIL(11_231_000_001_100_000_000_011n) - .toFemtoFIL() - .toString(), - '11231000001100000000.011' - ) - assert.strictEqual( - Token.fromAttoFIL(11_231_000_001_100_000_000_011n).toPicoFIL().toString(), - '11231000001100000.000011' - ) - assert.strictEqual( - Token.fromAttoFIL(11_231_000_001_100_000_000_011n).toNanoFIL().toString(), - '11231000001100.000000011' - ) - assert.strictEqual( - Token.fromAttoFIL(11_231_000_001_100_000_000_011n) - .toMicroFIL() - .toString(), - '11231000001.100000000011' - ) - assert.strictEqual( - Token.fromAttoFIL(11_231_000_001_100_000_000_011n) - .toMilliFIL() - .toString(), - '11231000.001100000000011' - ) - assert.strictEqual( - Token.fromAttoFIL(11_231_000_001_100_000_000_011n).toFIL().toString(), - '11231.000001100000000011' - ) - }) - - const vectors = [ - ['0', ''], - ['9', '0009'], - ['22', '0016'], - ['-18', '0112'], - ['-26118', '016606'], - ['-20368000000000000', '01485c968cc90000'], - ['23752000000000000', '0054625172b48000'], - ['4171000000000000', '000ed1809d5bb000'], - ['6098800000000000000000000', '00050b789c4844bc17c00000'], - ['-6180700000000000000000000', '01051cd06b1a8ff003f00000'], - ] - - for (const [atto, serialized] of vectors) { - it(`serialize ${atto}`, () => { - const s = base16.encode(Token.fromAttoFIL(atto).toBytes()).toLowerCase() - - assert.strictEqual(s, serialized) - }) - } - - it('should format', () => { - assert.strictEqual( - Token.fromAttoFIL(1).toFIL().toString(), - '0.000000000000000001' - ) - - assert.strictEqual( - Token.fromFIL(100).toFormat({ - decimalPlaces: 0, - groupSize: 0, - }), - Token.fromFIL(100).toString() - ) - - assert.strictEqual( - Token.fromAttoFIL(1).toFIL().toFormat({ - suffix: ' FIL', - }), - '0.000000000000000001 FIL' - ) - }) -}) diff --git a/packages/iso-filecoin/test/utils.test.js b/packages/iso-filecoin/test/utils.test.js deleted file mode 100644 index b58f007..0000000 --- a/packages/iso-filecoin/test/utils.test.js +++ /dev/null @@ -1,48 +0,0 @@ -import assert from 'assert' -import { parseDerivationPath } from '../src/utils.js' - -describe('derivation path', () => { - it('should parse testnet bip44 derivation path', () => { - const components = parseDerivationPath("m/44'/1'/0'/0/0") - - assert.deepStrictEqual(components, { - purpose: 44, - coinType: 1, - account: 0, - change: 0, - addressIndex: 0, - }) - }) - - it('should parse mainnet bip44 derivation path', () => { - const components = parseDerivationPath("m/44'/461'/0'/0/0") - - assert.deepStrictEqual(components, { - purpose: 44, - coinType: 461, - account: 0, - change: 0, - addressIndex: 0, - }) - }) - - it('should fail parse short bip44 derivation path', async () => { - assert.throws(() => parseDerivationPath("m/44'/461'/0'/0"), { - message: - "Invalid derivation path: depth must be 5 \"m / purpose' / coin_type' / account' / change / address_index\"", - }) - }) - - it('should fail parse bip44 derivation path without m', async () => { - assert.throws(() => parseDerivationPath("/44'/461'/0'/0/0"), { - message: 'Invalid derivation path: depth 0 must be "m"', - }) - }) - - it("should fail parse bip44 derivation path with part 1 != 44'", async () => { - assert.throws(() => parseDerivationPath("m/j4'/461'/0'/0/0"), { - message: - 'Invalid derivation path: The "purpose" node (depth 1) must be the string "44\'"', - }) - }) -}) diff --git a/packages/iso-filecoin/test/wallet.test.js b/packages/iso-filecoin/test/wallet.test.js deleted file mode 100644 index 7042249..0000000 --- a/packages/iso-filecoin/test/wallet.test.js +++ /dev/null @@ -1,103 +0,0 @@ -import assert from 'assert' -import { base16, base64pad } from 'iso-base/rfc4648' -import { Message } from '../src/message.js' -import * as Wallet from '../src/wallet.js' - -const mnemonic = - 'raw include ecology social turtle still perfect trip dance food welcome aunt patient very toss very program estate diet portion city camera loop guess' - -describe('wallet', () => { - it('should generate 24 word mnemonic', () => { - assert.strictEqual(Wallet.generateMnemonic().split(' ').length, 24) - }) - - it('should generate seed from mnemonic', () => { - const seed = Wallet.mnemonicToSeed(Wallet.generateMnemonic()) - assert.strictEqual(toString.call(seed).slice(8, -1), 'Uint8Array') - }) - - it('should generate create account from mnemonic', () => { - const account = Wallet.accountFromMnemonic( - mnemonic, - 'SECP256K1', - "m/44'/461'/0'/0/0" - ) - - assert.strictEqual( - account.address.toString(), - 'f17levgrkmq7jeloew44ixqokvl4qdozvmacidp7i' - ) - }) - - it('should generate create account from seed', () => { - const seed = Wallet.mnemonicToSeed(mnemonic) - const account = Wallet.accountFromSeed( - seed, - 'SECP256K1', - "m/44'/461'/0'/0/0" - ) - - assert.strictEqual( - account.address.toString(), - 'f17levgrkmq7jeloew44ixqokvl4qdozvmacidp7i' - ) - }) - - it('should generate create account from mnemonic metamask', () => { - const seed = Wallet.mnemonicToSeed( - 'already turtle birth enroll since owner keep patch skirt drift any dinner' - ) - const account = Wallet.accountFromSeed( - seed, - 'SECP256K1', - "m/44'/461'/0'/0/0" - ) - - assert.strictEqual( - account.address.toString(), - 'f1jbnosztqwadgh4smvsnojdvwjgqxsmtzy5n5imi' - ) - }) - - it('should sign', () => { - const account = Wallet.accountFromPrivateKey( - base64pad.decode('tI1wF8uJseC1QdNj3CbpBAVC8G9/pfgtSYt4yXlJ+UY='), - 'SECP256K1', - 'mainnet' - ) - - assert.deepEqual( - base64pad.encode(account.pubKey), - 'BLW+ZCazhsVWEuuwxt5DEcSyXnmpJGxFBizYf/pSiBKlXz9qgW9d4yN0Vm6WJ+D5G9c7WxWAO+mBL3RpjVEYR6E=' - ) - assert.deepEqual( - account.address.toString(), - 'f17dyptywvmnldq2fsm6j226txnltf4aiwsi3vlka' - ) - - const message = Message.fromLotus({ - Version: 0, - To: 'f1ypi542zmmgaltijzw4byonei5c267ev5iif2liy', - From: 'f17dyptywvmnldq2fsm6j226txnltf4aiwsi3vlka', - Value: '87316', - Params: '', - GasFeeCap: '42908', - GasPremium: '28871', - GasLimit: 20_982, - Nonce: 20_101, - Method: 65_360, - }) - - assert.deepEqual( - base16.encode(message.serialize()).toLowerCase(), - '8a005501c3d1de6b2c6180b9a139b703873488e8b5ef92bd5501f8f0f9e2d563563868b26793ad7a776ae65e0116194e8544000155141951f64300a79c430070c719ff5040' - ) - - const sig = Wallet.signMessage(account.privateKey, 'SECP256K1', message) - - assert.deepEqual( - base64pad.encode(sig), - 'jzg+/H2mHXezbUBAtQAYbj3MrwVn92mXFRw6FX2NRK1+Zfha2vSP23GVEkJHHXxyAd+IggjzG2L440fIJbdfSgA=' - ) - }) -}) diff --git a/packages/iso-filecoin/tsconfig.json b/packages/iso-filecoin/tsconfig.json deleted file mode 100644 index 3f6cc69..0000000 --- a/packages/iso-filecoin/tsconfig.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "emitDeclarationOnly": true, - "outDir": "dist" - }, - "references": [ - { - "path": "../iso-base" - }, - { - "path": "../iso-web" - } - ], - "include": ["src", "scripts", "test", "package.json"], - "exclude": ["node_modules", "dist", "out"], - "typedocOptions": { - "entryPointStrategy": "resolve", - "entryPoints": [ - "src/wallet.js", - "src/token.js", - "src/address.js", - "src/message.js", - "src/rpc.js", - "src/signature.js", - "src/utils.js" - ], - "includeVersion": true, - "excludeExternals": true, - "internalModule": "" - } -} diff --git a/readme.md b/readme.md index f9961c4..c9b4731 100644 --- a/readme.md +++ b/readme.md @@ -11,7 +11,6 @@ - [iso-signatures](https://github.com/hugomrdias/iso-repo/tree/main/packages/iso-signatures) - Isomorphic signatures tooling - [iso-web](https://github.com/hugomrdias/iso-repo/tree/main/packages/iso-signatures) - Isomorphic web apis utilities for fetch, signals, crypto and doh. - [iso-websocket](https://github.com/hugomrdias/iso-repo/tree/main/packages/iso-websocket) - `iso-websocket` implements the [Websocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) interface and is compatible with the browser, node.js and any other engine that implements basic Web APIs. -- [iso-filecoin](https://github.com/hugomrdias/filecoin) - Filecoin tooling was moved to its own repository ## Contributing diff --git a/tsconfig.json b/tsconfig.json index e4c229c..4ab1267 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,6 @@ "entryPointStrategy": "packages", "entryPoints": [ "packages/iso-base", - "packages/iso-filecoin", "packages/iso-passkeys", "packages/iso-did", "packages/iso-signatures",