diff --git a/.changeset/funny-jars-camp.md b/.changeset/funny-jars-camp.md new file mode 100644 index 0000000000..55be951fa8 --- /dev/null +++ b/.changeset/funny-jars-camp.md @@ -0,0 +1,8 @@ +--- +"@near-js/accounts": patch +"near-api-js": patch +"@near-js/providers": patch +"@near-js/utils": patch +--- + +add check for global 'process' object diff --git a/.changeset/nice-worms-arrive.md b/.changeset/nice-worms-arrive.md new file mode 100644 index 0000000000..f70e8e5881 --- /dev/null +++ b/.changeset/nice-worms-arrive.md @@ -0,0 +1,5 @@ +--- +"@near-js/biometric-ed25519": minor +--- + +Handing null response on create key flow diff --git a/.changeset/spicy-tomatoes-talk.md b/.changeset/spicy-tomatoes-talk.md new file mode 100644 index 0000000000..3084e92414 --- /dev/null +++ b/.changeset/spicy-tomatoes-talk.md @@ -0,0 +1,10 @@ +--- +"near-api-js": major +"@near-js/accounts": major +"@near-js/biometric-ed25519": major +"@near-js/crypto": major +"@near-js/transactions": major +"@near-js/wallet-account": major +--- + +feat: updated borsh-js to v1.0.1 diff --git a/.changeset/wet-jars-attend.md b/.changeset/wet-jars-attend.md new file mode 100644 index 0000000000..96a008cc77 --- /dev/null +++ b/.changeset/wet-jars-attend.md @@ -0,0 +1,6 @@ +--- +"near-api-js": patch +"@near-js/providers": patch +--- + +fixes override of global fetch property diff --git a/packages/accounts/package.json b/packages/accounts/package.json index 00a1b066bd..239fd836fc 100644 --- a/packages/accounts/package.json +++ b/packages/accounts/package.json @@ -25,7 +25,7 @@ "ajv": "^8.11.2", "ajv-formats": "^2.1.1", "bn.js": "5.2.1", - "borsh": "^0.7.0", + "borsh": "1.0.0", "depd": "^2.0.0", "near-abi": "0.1.1" }, diff --git a/packages/accounts/src/account.ts b/packages/accounts/src/account.ts index 2206212a74..837dca6678 100644 --- a/packages/accounts/src/account.ts +++ b/packages/accounts/src/account.ts @@ -26,6 +26,8 @@ import { BlockReference, } from '@near-js/types'; import { + baseDecode, + baseEncode, logWarning, parseResultError, DEFAULT_FUNCTION_CALL_GAS, @@ -33,7 +35,6 @@ import { printTxOutcomeLogsAndFailures, } from '@near-js/utils'; import BN from 'bn.js'; -import { baseDecode, baseEncode } from 'borsh'; import { Connection } from './connection'; @@ -363,7 +364,7 @@ export class Account { * @param beneficiaryId The NEAR account that will receive the remaining Ⓝ balance from the account being deleted */ async deleteAccount(beneficiaryId: string) { - if (!process.env['NEAR_NO_LOGS']) { + if (!(typeof process === 'object' && process.env['NEAR_NO_LOGS'])) { console.log('Deleting an account does not automatically transfer NFTs and FTs to the beneficiary address. Ensure to transfer assets before deleting.'); } return this.signAndSendTransaction({ diff --git a/packages/accounts/test/providers.test.js b/packages/accounts/test/providers.test.js index 68ad1d5dfe..67b39cc660 100644 --- a/packages/accounts/test/providers.test.js +++ b/packages/accounts/test/providers.test.js @@ -69,9 +69,8 @@ test('json rpc query view_state', withProvider(async (provider) => { block_height: expect.any(Number), block_hash: expect.any(String), values: [ - { key: 'bmFtZQ==', value: 'aGVsbG8=', proof: [] } - ], - proof: [] + { key: 'bmFtZQ==', value: 'aGVsbG8=' } + ] }); }); })); diff --git a/packages/biometric-ed25519/package.json b/packages/biometric-ed25519/package.json index d846d51fcb..136f655243 100644 --- a/packages/biometric-ed25519/package.json +++ b/packages/biometric-ed25519/package.json @@ -1,7 +1,7 @@ { "name": "@near-js/biometric-ed25519", "description": "JavaScript library to handle webauthn and biometric keys", - "version": "0.3.0", + "version": "0.4.0", "main": "lib/index.js", "types": "lib/index.d.ts", "scripts": { @@ -16,9 +16,10 @@ "@aws-crypto/sha256-js": "^4.0.0", "@hexagon/base64": "^1.1.26", "@near-js/crypto": "workspace:*", + "@near-js/utils": "workspace:*", "asn1-parser": "^1.1.8", "bn.js": "5.2.1", - "borsh": "^0.7.0", + "borsh": "1.0.0", "buffer": "^6.0.3", "elliptic": "^6.5.4", "fido2-lib": "3.4.1" diff --git a/packages/biometric-ed25519/src/index.ts b/packages/biometric-ed25519/src/index.ts index 4076e1fb94..29106ca3a4 100644 --- a/packages/biometric-ed25519/src/index.ts +++ b/packages/biometric-ed25519/src/index.ts @@ -4,7 +4,7 @@ import { Sha256 } from '@aws-crypto/sha256-js'; import { Buffer } from 'buffer'; import asn1 from 'asn1-parser'; import { KeyPair } from '@near-js/crypto'; -import { baseEncode } from 'borsh'; +import { baseEncode } from '@near-js/utils'; import { validateUsername, preformatMakeCredReq, @@ -34,6 +34,13 @@ function setBufferIfUndefined() { } } +export class PasskeyProcessCanceled extends Error { + constructor(message) { + super(message); + this.name = 'PasskeyProcessCanceled'; + } +} + export const createKey = async (username: string): Promise => { const cleanUserName = validateUsername(username); if (!f2l.f2l) { @@ -51,6 +58,10 @@ export const createKey = async (username: string): Promise => { setBufferIfUndefined(); return navigator.credentials.create({ publicKey }) .then(async (res) => { + if (!res) { + throw new PasskeyProcessCanceled('Failed to retrieve response from navigator.credentials.create'); + } + const result = await f2l.attestation({ clientAttestationResponse: res, origin, diff --git a/packages/crypto/package.json b/packages/crypto/package.json index 31ceacf050..8757b4e603 100644 --- a/packages/crypto/package.json +++ b/packages/crypto/package.json @@ -17,8 +17,9 @@ "license": "ISC", "dependencies": { "@near-js/types": "workspace:*", + "@near-js/utils": "workspace:*", "bn.js": "5.2.1", - "borsh": "^0.7.0", + "borsh": "1.0.0", "tweetnacl": "^1.0.1" }, "devDependencies": { diff --git a/packages/crypto/src/key_pair_ed25519.ts b/packages/crypto/src/key_pair_ed25519.ts index 4bcd01379b..031b106724 100644 --- a/packages/crypto/src/key_pair_ed25519.ts +++ b/packages/crypto/src/key_pair_ed25519.ts @@ -1,4 +1,4 @@ -import { baseEncode, baseDecode } from 'borsh'; +import { baseEncode, baseDecode } from '@near-js/utils'; import nacl from 'tweetnacl'; import { KeyType } from './constants'; diff --git a/packages/crypto/src/public_key.ts b/packages/crypto/src/public_key.ts index f9e8bc48c9..2e337787df 100644 --- a/packages/crypto/src/public_key.ts +++ b/packages/crypto/src/public_key.ts @@ -1,5 +1,5 @@ import { Assignable } from '@near-js/types'; -import { baseEncode, baseDecode } from 'borsh'; +import { baseEncode, baseDecode } from '@near-js/utils'; import nacl from 'tweetnacl'; import { KeyType } from './constants'; diff --git a/packages/crypto/test/key_pair.test.js b/packages/crypto/test/key_pair.test.js index 12e3ec0cbf..7e48204a09 100644 --- a/packages/crypto/test/key_pair.test.js +++ b/packages/crypto/test/key_pair.test.js @@ -1,4 +1,4 @@ -const { baseEncode } = require('borsh'); +const { baseEncode } = require('@near-js/utils'); const { sha256 } = require('js-sha256'); const { KeyPair, KeyPairEd25519, PublicKey } = require('../lib'); diff --git a/packages/near-api-js/package.json b/packages/near-api-js/package.json index 779aad46b3..d64ded4884 100644 --- a/packages/near-api-js/package.json +++ b/packages/near-api-js/package.json @@ -25,7 +25,7 @@ "ajv": "^8.11.2", "ajv-formats": "^2.1.1", "bn.js": "5.2.1", - "borsh": "^0.7.0", + "borsh": "1.0.0", "depd": "^2.0.0", "error-polyfill": "^0.1.3", "http-errors": "^1.7.2", diff --git a/packages/near-api-js/src/connect.ts b/packages/near-api-js/src/connect.ts index cc4f36e3fc..f46971eee1 100644 --- a/packages/near-api-js/src/connect.ts +++ b/packages/near-api-js/src/connect.ts @@ -25,11 +25,8 @@ import { readKeyFile } from './key_stores/unencrypted_file_system_keystore'; import { InMemoryKeyStore, MergeKeyStore } from './key_stores'; import { Near, NearConfig } from './near'; -import fetch from './utils/setup-node-fetch'; import { logWarning } from './utils'; -global.fetch = fetch; - export interface ConnectConfig extends NearConfig { /** * Initialize an {@link key_stores/in_memory_key_store!InMemoryKeyStore} by reading the file at keyPath. @@ -57,7 +54,7 @@ export async function connect(config: ConnectConfig): Promise { keyPathStore, config.keyStore || config.deps?.keyStore ], { writeKeyStoreIndex: 1 }); - if (!process.env['NEAR_NO_LOGS']) { + if (!(typeof process === 'object' && process.env['NEAR_NO_LOGS'])) { console.log(`Loaded master account ${accountKeyFile[0]} key from ${config.keyPath} with public key = ${keyPair.getPublicKey()}`); } } diff --git a/packages/near-api-js/src/utils/serialize.ts b/packages/near-api-js/src/utils/serialize.ts index 8fa8a2542e..a2d1eafa14 100644 --- a/packages/near-api-js/src/utils/serialize.ts +++ b/packages/near-api-js/src/utils/serialize.ts @@ -1,10 +1,10 @@ export { - baseEncode as base_encode, - baseDecode as base_decode, serialize, deserialize, Schema, - BorshError, - BinaryWriter, - BinaryReader, -} from 'borsh'; \ No newline at end of file +} from 'borsh'; + +export { + baseEncode as base_encode, + baseDecode as base_decode, +} from '@near-js/utils'; \ No newline at end of file diff --git a/packages/providers/package.json b/packages/providers/package.json index 9e3129cfc6..180fef8346 100644 --- a/packages/providers/package.json +++ b/packages/providers/package.json @@ -20,7 +20,7 @@ "@near-js/types": "workspace:*", "@near-js/utils": "workspace:*", "bn.js": "5.2.1", - "borsh": "^0.7.0", + "borsh": "1.0.0", "http-errors": "^1.7.2" }, "devDependencies": { diff --git a/packages/providers/src/fetch_json.ts b/packages/providers/src/fetch_json.ts index 386143c14c..bef1a029dc 100644 --- a/packages/providers/src/fetch_json.ts +++ b/packages/providers/src/fetch_json.ts @@ -16,7 +16,7 @@ export interface ConnectionInfo { headers?: { [key: string]: string | number }; } -const logWarning = (...args) => !process.env['NEAR_NO_LOGS'] && console.warn(...args); +const logWarning = (...args) => !(typeof process === 'object' && process.env['NEAR_NO_LOGS']) && console.warn(...args); export async function fetchJson(connectionInfoOrUrl: string | ConnectionInfo, json?: string): Promise { let connectionInfo: ConnectionInfo = { url: null }; @@ -28,11 +28,8 @@ export async function fetchJson(connectionInfoOrUrl: string | ConnectionInfo, js const response = await exponentialBackoff(START_WAIT_TIME_MS, RETRY_NUMBER, BACKOFF_MULTIPLIER, async () => { try { - if (!global.fetch) { - global.fetch = (await import('./fetch')).default; - } - const response = await global.fetch(connectionInfo.url, { + const response = await (global.fetch ?? (await import('./fetch')).default)(connectionInfo.url, { method: json ? 'POST' : 'GET', body: json ? json : undefined, headers: { ...connectionInfo.headers, 'Content-Type': 'application/json' } diff --git a/packages/providers/src/json-rpc-provider.ts b/packages/providers/src/json-rpc-provider.ts index ed379a5a9a..921c860612 100644 --- a/packages/providers/src/json-rpc-provider.ts +++ b/packages/providers/src/json-rpc-provider.ts @@ -6,6 +6,7 @@ * @see {@link providers/provider | providers} for a list of request and response types */ import { + baseEncode, getErrorTypeFromErrorMessage, parseRpcError, } from '@near-js/utils'; @@ -34,7 +35,6 @@ import { encodeTransaction, SignedTransaction, } from '@near-js/transactions'; -import { baseEncode } from 'borsh'; import { exponentialBackoff } from './exponential-backoff'; import { Provider } from './provider'; @@ -372,7 +372,7 @@ export class JsonRpcProvider extends Provider { return response; } catch (error) { if (error.type === 'TimeoutError') { - if (!process.env['NEAR_NO_LOGS']) { + if (!(typeof process === 'object' && process.env['NEAR_NO_LOGS'])) { console.warn(`Retrying request to ${method} as it has timed out`, params); } return null; diff --git a/packages/transactions/package.json b/packages/transactions/package.json index 40880a7219..1907108705 100644 --- a/packages/transactions/package.json +++ b/packages/transactions/package.json @@ -21,7 +21,7 @@ "@near-js/types": "workspace:*", "@near-js/utils": "workspace:*", "bn.js": "5.2.1", - "borsh": "^0.7.0", + "borsh": "1.0.0", "js-sha256": "^0.9.0" }, "devDependencies": { diff --git a/packages/transactions/src/action_creators.ts b/packages/transactions/src/action_creators.ts index 08457cf96c..6f5ee1f36a 100644 --- a/packages/transactions/src/action_creators.ts +++ b/packages/transactions/src/action_creators.ts @@ -22,6 +22,7 @@ import { Signature } from './signature'; function fullAccessKey(): AccessKey { return new AccessKey({ + nonce: 0, permission: new AccessKeyPermission({ fullAccess: new FullAccessPermission({}), }) @@ -30,6 +31,7 @@ function fullAccessKey(): AccessKey { function functionCallAccessKey(receiverId: string, methodNames: string[], allowance?: BN): AccessKey { return new AccessKey({ + nonce: 0, permission: new AccessKeyPermission({ functionCall: new FunctionCallPermission({ receiverId, allowance, methodNames }), }) @@ -60,7 +62,7 @@ export function stringifyJsonOrBytes(args: any): Buffer { * @param stringify Convert input arguments into bytes array. * @param jsContract Is contract from JS SDK, skips stringification of arguments. */ -function functionCall(methodName: string, args: Uint8Array | object, gas: BN, deposit: BN, stringify = stringifyJsonOrBytes, jsContract = false): Action { +function functionCall(methodName: string, args: Uint8Array | object, gas: BN = new BN(0), deposit: BN = new BN(0), stringify = stringifyJsonOrBytes, jsContract = false): Action { if(jsContract){ return new Action({ functionCall: new FunctionCall({ methodName, args, gas, deposit }) }); } @@ -75,11 +77,11 @@ function functionCall(methodName: string, args: Uint8Array | object, gas: BN, de }); } -function transfer(deposit: BN): Action { +function transfer(deposit: BN = new BN(0)): Action { return new Action({ transfer: new Transfer({ deposit }) }); } -function stake(stake: BN, publicKey: PublicKey): Action { +function stake(stake: BN = new BN(0), publicKey: PublicKey): Action { return new Action({ stake: new Stake({ stake, publicKey }) }); } diff --git a/packages/transactions/src/actions.ts b/packages/transactions/src/actions.ts index ee413a5333..3a161be5fc 100644 --- a/packages/transactions/src/actions.ts +++ b/packages/transactions/src/actions.ts @@ -33,6 +33,7 @@ export class AccessKeyPermission extends Enum { } export class AccessKey extends Assignable { + nonce: BN; permission: AccessKeyPermission; } diff --git a/packages/transactions/src/create_transaction.ts b/packages/transactions/src/create_transaction.ts index 9f90de00ff..5d5d1198af 100644 --- a/packages/transactions/src/create_transaction.ts +++ b/packages/transactions/src/create_transaction.ts @@ -5,5 +5,5 @@ import { Action } from './actions'; import { Transaction } from './schema'; export function createTransaction(signerId: string, publicKey: PublicKey, receiverId: string, nonce: BN | string | number, actions: Action[], blockHash: Uint8Array): Transaction { - return new Transaction({ signerId, publicKey, nonce, receiverId, actions, blockHash }); + return new Transaction({ signerId, publicKey, nonce: new BN(nonce), receiverId, actions, blockHash }); } diff --git a/packages/transactions/src/schema.ts b/packages/transactions/src/schema.ts index ae7bafab61..39690ae92b 100644 --- a/packages/transactions/src/schema.ts +++ b/packages/transactions/src/schema.ts @@ -1,23 +1,11 @@ import { PublicKey } from '@near-js/crypto'; import { Assignable } from '@near-js/types'; -import { deserialize, serialize } from 'borsh'; +import { deserialize, serialize, Schema } from 'borsh'; import BN from 'bn.js'; import { Action, - AccessKey, - AccessKeyPermission, - AddKey, - CreateAccount, - DeleteAccount, - DeleteKey, - DeployContract, - FullAccessPermission, - FunctionCall, - FunctionCallPermission, SignedDelegate, - Stake, - Transfer, } from './actions'; import { DelegateAction } from './delegate'; import { DelegateActionPrefix } from './prefix'; @@ -31,8 +19,8 @@ import { Signature } from './signature'; */ export function encodeDelegateAction(delegateAction: DelegateAction) { return new Uint8Array([ - ...serialize(SCHEMA, new DelegateActionPrefix()), - ...serialize(SCHEMA, delegateAction), + ...serialize(SCHEMA.DelegateActionPrefix, new DelegateActionPrefix()), + ...serialize(SCHEMA.DelegateAction, delegateAction), ]); } @@ -41,27 +29,28 @@ export function encodeDelegateAction(delegateAction: DelegateAction) { * @param signedDelegate Signed delegate to be executed in a meta transaction */ export function encodeSignedDelegate(signedDelegate: SignedDelegate) { - return serialize(SCHEMA, signedDelegate); + return serialize(SCHEMA.SignedDelegate, signedDelegate); } export function encodeTransaction(transaction: Transaction | SignedTransaction) { - return serialize(SCHEMA, transaction); + const schema: Schema = transaction instanceof SignedTransaction ? SCHEMA.SignedTransaction : SCHEMA.Transaction; + return serialize(schema, transaction); } /** * Borsh-decode a Transaction instance from a buffer - * @param bytes Buffer data to be decoded + * @param bytes Uint8Array data to be decoded */ -export function decodeTransaction(bytes: Buffer) { - return deserialize(SCHEMA, Transaction, bytes); +export function decodeTransaction(bytes: Uint8Array) { + return new Transaction(deserialize(SCHEMA.Transaction, bytes)); } /** * Borsh-decode a SignedTransaction instance from a buffer - * @param bytes Buffer data to be decoded + * @param bytes Uint8Array data to be decoded */ -export function decodeSignedTransaction(bytes: Buffer) { - return deserialize(SCHEMA, SignedTransaction, bytes); +export function decodeSignedTransaction(bytes: Uint8Array) { + return new SignedTransaction(deserialize(SCHEMA.SignedTransaction, bytes)); } export class Transaction extends Assignable { @@ -72,11 +61,11 @@ export class Transaction extends Assignable { actions: Action[]; blockHash: Uint8Array; - encode() { + encode(): Uint8Array { return encodeTransaction(this); } - static decode(bytes: Buffer) { + static decode(bytes: Uint8Array): Transaction { return decodeTransaction(bytes); } } @@ -85,103 +74,153 @@ export class SignedTransaction extends Assignable { transaction: Transaction; signature: Signature; - encode() { + encode(): Uint8Array{ return encodeTransaction(this); } - static decode(bytes: Buffer) { + static decode(bytes: Uint8Array): SignedTransaction { return decodeSignedTransaction(bytes); } } -type Class = new (...args: any[]) => T; - -export const SCHEMA = new Map([ - [Signature, { kind: 'struct', fields: [ - ['keyType', 'u8'], - ['data', [64]] - ] }], - [SignedTransaction, { kind: 'struct', fields: [ - ['transaction', Transaction], - ['signature', Signature] - ] }], - [Transaction, { kind: 'struct', fields: [ - ['signerId', 'string'], - ['publicKey', PublicKey], - ['nonce', 'u64'], - ['receiverId', 'string'], - ['blockHash', [32]], - ['actions', [Action]] - ] }], - [PublicKey, { kind: 'struct', fields: [ - ['keyType', 'u8'], - ['data', [32]] - ] }], - [AccessKey, { kind: 'struct', fields: [ - ['nonce', 'u64'], - ['permission', AccessKeyPermission], - ] }], - [AccessKeyPermission, { kind: 'enum', field: 'enum', values: [ - ['functionCall', FunctionCallPermission], - ['fullAccess', FullAccessPermission], - ] }], - [FunctionCallPermission, { kind: 'struct', fields: [ - ['allowance', { kind: 'option', type: 'u128' }], - ['receiverId', 'string'], - ['methodNames', ['string']], - ] }], - [FullAccessPermission, { kind: 'struct', fields: [] }], - [Action, { kind: 'enum', field: 'enum', values: [ - ['createAccount', CreateAccount], - ['deployContract', DeployContract], - ['functionCall', FunctionCall], - ['transfer', Transfer], - ['stake', Stake], - ['addKey', AddKey], - ['deleteKey', DeleteKey], - ['deleteAccount', DeleteAccount], - ['signedDelegate', SignedDelegate], - ] }], - [CreateAccount, { kind: 'struct', fields: [] }], - [DeployContract, { kind: 'struct', fields: [ - ['code', ['u8']] - ] }], - [FunctionCall, { kind: 'struct', fields: [ - ['methodName', 'string'], - ['args', ['u8']], - ['gas', 'u64'], - ['deposit', 'u128'] - ] }], - [Transfer, { kind: 'struct', fields: [ - ['deposit', 'u128'] - ] }], - [Stake, { kind: 'struct', fields: [ - ['stake', 'u128'], - ['publicKey', PublicKey] - ] }], - [AddKey, { kind: 'struct', fields: [ - ['publicKey', PublicKey], - ['accessKey', AccessKey] - ] }], - [DeleteKey, { kind: 'struct', fields: [ - ['publicKey', PublicKey] - ] }], - [DeleteAccount, { kind: 'struct', fields: [ - ['beneficiaryId', 'string'] - ] }], - [DelegateAction, { kind: 'struct', fields: [ - ['senderId', 'string'], - ['receiverId', 'string'], - ['actions', [Action]], - ['nonce', 'u64'], - ['maxBlockHeight', 'u64'], - ['publicKey', PublicKey], - ] }], - [DelegateActionPrefix, { kind: 'struct', fields: [ - ['prefix', 'u32'], - ] }], - [SignedDelegate, { kind: 'struct', fields: [ - ['delegateAction', DelegateAction], - ['signature', Signature], - ] }], -]); +export const SCHEMA = new class BorshSchema { + Signature: Schema = { + struct: { + keyType: 'u8', + data: { array: { type: 'u8', len: 64 } }, + } + }; + PublicKey: Schema = { + struct: { + keyType: 'u8', + data: { array: { type: 'u8', len: 32 } }, + } + }; + FunctionCallPermission: Schema = { + struct: { + allowance: { option: 'u128' }, + receiverId: 'string', + methodNames: { array: { type: 'string' } }, + } + }; + FullAccessPermission: Schema = { + struct: {} + }; + AccessKeyPermission: Schema = { + enum: [ + { struct: { functionCall: this.FunctionCallPermission } }, + { struct: { fullAccess: this.FullAccessPermission } }, + ] + }; + AccessKey: Schema = { + struct: { + nonce: 'u64', + permission: this.AccessKeyPermission, + } + }; + CreateAccount: Schema = { + struct: {} + }; + DeployContract: Schema = { + struct: { + code: { array: { type: 'u8' } }, + } + }; + FunctionCall: Schema = { + struct: { + methodName: 'string', + args: { array: { type: 'u8' } }, + gas: 'u64', + deposit: 'u128', + } + }; + Transfer: Schema = { + struct: { + deposit: 'u128', + } + }; + Stake: Schema = { + struct: { + stake: 'u128', + publicKey: this.PublicKey, + } + }; + AddKey: Schema = { + struct: { + publicKey: this.PublicKey, + accessKey: this.AccessKey, + } + }; + DeleteKey: Schema = { + struct: { + publicKey: this.PublicKey, + } + }; + DeleteAccount: Schema = { + struct: { + beneficiaryId: 'string', + } + }; + DelegateActionPrefix: Schema = { + struct: { + prefix: 'u32', + } + }; + ClassicActions: Schema = { + enum: [ + { struct: { createAccount: this.CreateAccount } }, + { struct: { deployContract: this.DeployContract } }, + { struct: { functionCall: this.FunctionCall } }, + { struct: { transfer: this.Transfer } }, + { struct: { stake: this.Stake } }, + { struct: { addKey: this.AddKey } }, + { struct: { deleteKey: this.DeleteKey } }, + { struct: { deleteAccount: this.DeleteAccount } }, + ] + }; + DelegateAction: Schema = { + struct: { + senderId: 'string', + receiverId: 'string', + nonce: 'u64', + actions: { array: { type: this.ClassicActions } }, + maxBlockHeight: 'u64', + publicKey: this.PublicKey, + } + }; + SignedDelegate: Schema = { + struct: { + delegateAction: this.DelegateAction, + signature: this.Signature, + } + }; + Action: Schema = { + enum: [ + { struct: { createAccount: this.CreateAccount } }, + { struct: { deployContract: this.DeployContract } }, + { struct: { functionCall: this.FunctionCall } }, + { struct: { transfer: this.Transfer } }, + { struct: { stake: this.Stake } }, + { struct: { addKey: this.AddKey } }, + { struct: { deleteKey: this.DeleteKey } }, + { struct: { deleteAccount: this.DeleteAccount } }, + { struct: { signedDelegate: this.SignedDelegate } }, + ] + }; + Transaction: Schema = { + struct: { + signerId: 'string', + publicKey: this.PublicKey, + nonce: 'u64', + receiverId: 'string', + blockHash: { array: { type: 'u8', len: 32 } }, + actions: { array: { type: this.Action } }, + } + }; + SignedTransaction: Schema = { + struct: { + transaction: this.Transaction, + signature: this.Signature, + } + }; +}; diff --git a/packages/transactions/test/serialize.test.js b/packages/transactions/test/serialize.test.js index 5b2b0feb9d..78d34a47a5 100644 --- a/packages/transactions/test/serialize.test.js +++ b/packages/transactions/test/serialize.test.js @@ -2,9 +2,10 @@ const { KeyPair, PublicKey } = require('@near-js/crypto'); const { InMemoryKeyStore } = require('@near-js/keystores'); const { InMemorySigner } = require('@near-js/signers'); const { Assignable } = require('@near-js/types'); +const { baseDecode, baseEncode } = require('@near-js/utils'); const fs = require('fs'); const BN = require('bn.js'); -const { baseDecode, baseEncode, deserialize, serialize } = require('borsh'); +const { deserialize, serialize } = require('borsh'); const { actionCreators, @@ -33,16 +34,16 @@ class Test extends Assignable { test('serialize object', async () => { const value = new Test({ x: 255, y: 20, z: '123', q: [1, 2, 3] }); - const schema = new Map([[Test, { kind: 'struct', fields: [['x', 'u8'], ['y', 'u64'], ['z', 'string'], ['q', [3]]] }]]); + const schema = { struct: { x: 'u8', y: 'u16', z: 'string', q: { array: { type: 'u8' } } } }; let buf = serialize(schema, value); - let new_value = deserialize(schema, Test, buf); + let new_value = new Test(deserialize(schema, buf)); expect(new_value.x).toEqual(255); expect(new_value.y.toString()).toEqual('20'); expect(new_value.z).toEqual('123'); - expect(new_value.q).toEqual(new Uint8Array([1, 2, 3])); + expect(new_value.q).toEqual([1, 2, 3]); }); -test('serialize and sign multi-action tx', async() => { +test('serialize and sign multi-action tx', async () => { const keyStore = new InMemoryKeyStore(); const keyPair = KeyPair.fromString('ed25519:2wyRcSwSuHtRVmkMCGjPwnzZmQLeXLzLLyED1NDMt4BjnKgQL6tF85yBx6Jr26D2dUNeC716RBoTxntVHsegogYw'); await keyStore.setKey('test', 'test.near', keyPair); @@ -60,7 +61,7 @@ test('serialize and sign multi-action tx', async() => { const blockHash = baseDecode('244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM'); let [hash, { transaction }] = await signTransaction('123', 1, actions, blockHash, new InMemorySigner(keyStore), 'test.near', 'test'); expect(baseEncode(hash)).toEqual('Fo3MJ9XzKjnKuDuQKhDAC6fra5H2UWawRejFSEpPNk3Y'); - const serialized = serialize(SCHEMA, transaction); + const serialized = Buffer.from(serialize(SCHEMA.Transaction, transaction)); expect(serialized.toString('hex')).toEqual('09000000746573742e6e656172000f56a5f028dfc089ec7c39c1183b321b4d8f89ba5bec9e1762803cc2491f6ef80100000000000000030000003132330fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef608000000000103000000010203020300000071717103000000010203e80300000000000040420f00000000000000000000000000037b0000000000000000000000000000000440420f00000000000000000000000000000f56a5f028dfc089ec7c39c1183b321b4d8f89ba5bec9e1762803cc2491f6ef805000f56a5f028dfc089ec7c39c1183b321b4d8f89ba5bec9e1762803cc2491f6ef800000000000000000000030000007a7a7a010000000300000077777706000f56a5f028dfc089ec7c39c1183b321b4d8f89ba5bec9e1762803cc2491f6ef80703000000313233'); }); @@ -78,11 +79,11 @@ function createTransferTx() { blockHash); } -test('serialize transfer tx', async() => { +test('serialize transfer tx', async () => { const transaction = createTransferTx(); const serialized = encodeTransaction(transaction); - expect(serialized.toString('hex')).toEqual('09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef6010000000301000000000000000000000000000000'); + expect(Buffer.from(serialized).toString('hex')).toEqual('09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef6010000000301000000000000000000000000000000'); const deserialized = decodeTransaction(serialized); expect(encodeTransaction(deserialized)).toEqual(serialized); @@ -98,13 +99,13 @@ async function createKeyStore() { async function verifySignedTransferTx(signedTx) { expect(Buffer.from(signedTx.signature.data).toString('base64')).toEqual('lpqDMyGG7pdV5IOTJVJYBuGJo9LSu0tHYOlEQ+l+HE8i3u7wBZqOlxMQDtpuGRRNp+ig735TmyBwi6HY0CG9AQ=='); const serialized = encodeTransaction(signedTx); - expect(serialized.toString('hex')).toEqual('09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef601000000030100000000000000000000000000000000969a83332186ee9755e4839325525806e189a3d2d2bb4b4760e94443e97e1c4f22deeef0059a8e9713100eda6e19144da7e8a0ef7e539b20708ba1d8d021bd01'); - + expect(Buffer.from(serialized).toString('hex')).toEqual('09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef601000000030100000000000000000000000000000000969a83332186ee9755e4839325525806e189a3d2d2bb4b4760e94443e97e1c4f22deeef0059a8e9713100eda6e19144da7e8a0ef7e539b20708ba1d8d021bd01'); + const deserialized = decodeSignedTransaction(serialized); expect(encodeTransaction(deserialized)).toEqual(serialized); } -test('serialize and sign transfer tx', async() => { +test('serialize and sign transfer tx', async () => { const transaction = createTransferTx(); const keyStore = await createKeyStore(); @@ -113,7 +114,7 @@ test('serialize and sign transfer tx', async() => { verifySignedTransferTx(signedTx); }); -test('serialize and sign transfer tx object', async() => { +test('serialize and sign transfer tx object', async () => { const transaction = createTransferTx(); const keyStore = await createKeyStore(); @@ -127,12 +128,12 @@ describe('roundtrip test', () => { const testFiles = fs.readdirSync(dataDir); for (const testFile of testFiles) { if (/.+\.json$/.test(testFile)) { - const testDefinition = JSON.parse(fs.readFileSync(dataDir + '/' + testFile)); + const testDefinition = JSON.parse(fs.readFileSync(dataDir + '/' + testFile)); test(testFile, () => { const data = Buffer.from(testDefinition.data, 'hex'); - const type = Array.from(SCHEMA.keys()).find(key => key.name === testDefinition.type); - const deserialized = deserialize(SCHEMA, type, data); - const serialized = serialize(SCHEMA, deserialized); + const type = testDefinition.type; + const deserialized = deserialize(SCHEMA[type], Uint8Array.from(data)); + const serialized = Buffer.from(serialize(SCHEMA[type], deserialized)); expect(serialized).toEqual(data); }); } @@ -145,7 +146,7 @@ describe('serialize and deserialize on different types of nonce', () => { ]; const blockHash = baseDecode('244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM'); const targetNonce = new BN(1); - test('number typed nonce', async() => { + test('number typed nonce', async () => { const transaction = createTransaction( 'test.near', PublicKey.fromString('Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC'), @@ -154,13 +155,13 @@ describe('serialize and deserialize on different types of nonce', () => { actions, blockHash); const serialized = encodeTransaction(transaction); - expect(serialized.toString('hex')).toEqual('09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef6010000000301000000000000000000000000000000'); + expect(Buffer.from(serialized).toString('hex')).toEqual('09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef6010000000301000000000000000000000000000000'); const deserialized = decodeTransaction(serialized); expect(encodeTransaction(deserialized)).toEqual(serialized); expect(deserialized.nonce.toString()).toEqual(targetNonce.toString()); - }); - test('string typed nonce', async() => { + + test('string typed nonce', async () => { const transaction = createTransaction( 'test.near', PublicKey.fromString('Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC'), @@ -169,12 +170,13 @@ describe('serialize and deserialize on different types of nonce', () => { actions, blockHash); const serialized = encodeTransaction(transaction); - expect(serialized.toString('hex')).toEqual('09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef6010000000301000000000000000000000000000000'); + expect(Buffer.from(serialized).toString('hex')).toEqual('09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef6010000000301000000000000000000000000000000'); const deserialized = decodeTransaction(serialized); expect(encodeTransaction(deserialized)).toEqual(serialized); expect(deserialized.nonce.toString()).toEqual(targetNonce.toString()); }); - test('BN typed nonce', async() => { + + test('BN typed nonce', async () => { const transaction = createTransaction( 'test.near', PublicKey.fromString('Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC'), @@ -183,7 +185,7 @@ describe('serialize and deserialize on different types of nonce', () => { actions, blockHash); const serialized = encodeTransaction(transaction); - expect(serialized.toString('hex')).toEqual('09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef6010000000301000000000000000000000000000000'); + expect(Buffer.from(serialized).toString('hex')).toEqual('09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef6010000000301000000000000000000000000000000'); const deserialized = decodeTransaction(serialized); expect(encodeTransaction(deserialized)).toEqual(serialized); expect(deserialized.nonce.toString()).toEqual(targetNonce.toString()); diff --git a/packages/utils/package.json b/packages/utils/package.json index e43e81f155..158f01da0f 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -16,6 +16,7 @@ "dependencies": { "@near-js/types": "workspace:*", "bn.js": "5.2.1", + "bs58": "^4.0.0", "depd": "^2.0.0", "mustache": "^4.0.0" }, diff --git a/packages/utils/src/errors/errors.ts b/packages/utils/src/errors/errors.ts index d6c9c5bbc3..8173ea2e65 100644 --- a/packages/utils/src/errors/errors.ts +++ b/packages/utils/src/errors/errors.ts @@ -1,5 +1,5 @@ export function logWarning(...args: any[]): void { - if (!process.env['NEAR_NO_LOGS']){ + if (!(typeof process === 'object' && process.env['NEAR_NO_LOGS'])){ console.warn(...args); } } diff --git a/packages/utils/src/format.ts b/packages/utils/src/format.ts index 9e68d0b1b8..1013c332fb 100644 --- a/packages/utils/src/format.ts +++ b/packages/utils/src/format.ts @@ -1,4 +1,5 @@ import BN from 'bn.js'; +import bs58 from 'bs58'; /** * Exponent for calculating how many indivisible units are there in one NEAR. See {@link NEAR_NOMINATION}. @@ -105,3 +106,28 @@ function formatWithCommas(value: string): string { } return value; } + +/** + * Encodes a Uint8Array or string into base58 + * @param value Uint8Array or string representing a borsh encoded object + * @returns string base58 encoding of the value + */ +export function baseEncode(value: Uint8Array | string): string { + if (typeof value === 'string') { + const bytes = []; + for(let c = 0; c < value.length; c++){ + bytes.push(value.charCodeAt(c)); + } + value = new Uint8Array(bytes); + } + return bs58.encode(value); +} + +/** + * Decodes a base58 string into a Uint8Array + * @param value base58 encoded string + * @returns Uint8Array representing the decoded value + */ +export function baseDecode(value: string): Uint8Array { + return new Uint8Array(bs58.decode(value)); +} \ No newline at end of file diff --git a/packages/utils/src/logging.ts b/packages/utils/src/logging.ts index 5024a7559f..4b134562b7 100644 --- a/packages/utils/src/logging.ts +++ b/packages/utils/src/logging.ts @@ -2,7 +2,7 @@ import { FinalExecutionOutcome } from '@near-js/types'; import { parseRpcError } from './errors'; -const SUPPRESS_LOGGING = !!process.env.NEAR_NO_LOGS; +const SUPPRESS_LOGGING = !!(typeof process === 'object' && process.env.NEAR_NO_LOGS); /** * Parse and print details from a query execution response diff --git a/packages/wallet-account/package.json b/packages/wallet-account/package.json index 9985f8569f..b9f468d37d 100644 --- a/packages/wallet-account/package.json +++ b/packages/wallet-account/package.json @@ -20,7 +20,7 @@ "@near-js/types": "workspace:*", "@near-js/utils": "workspace:*", "bn.js": "5.2.1", - "borsh": "^0.7.0" + "borsh": "1.0.0" }, "devDependencies": { "@types/node": "^18.11.18", diff --git a/packages/wallet-account/src/wallet_account.ts b/packages/wallet-account/src/wallet_account.ts index fb495df7f1..9d27e9bd25 100644 --- a/packages/wallet-account/src/wallet_account.ts +++ b/packages/wallet-account/src/wallet_account.ts @@ -15,9 +15,10 @@ import { KeyPair, PublicKey } from '@near-js/crypto'; import { KeyStore } from '@near-js/keystores'; import { InMemorySigner } from '@near-js/signers'; import { FinalExecutionOutcome } from '@near-js/types'; +import { baseDecode } from '@near-js/utils'; import { Transaction, Action, SCHEMA, createTransaction } from '@near-js/transactions'; import BN from 'bn.js'; -import { baseDecode, serialize } from 'borsh'; +import { serialize } from 'borsh'; import { Near } from './near'; @@ -215,7 +216,7 @@ export class WalletConnection { const newUrl = new URL('sign', this._walletBaseUrl); newUrl.searchParams.set('transactions', transactions - .map(transaction => serialize(SCHEMA, transaction)) + .map(transaction => serialize(SCHEMA.Transaction, transaction)) .map(serialized => Buffer.from(serialized).toString('base64')) .join(',')); newUrl.searchParams.set('callbackUrl', callbackUrl || currentUrl.href); diff --git a/packages/wallet-account/test/wallet_account.test.js b/packages/wallet-account/test/wallet_account.test.js index 8500b52657..a01a8b0c04 100644 --- a/packages/wallet-account/test/wallet_account.test.js +++ b/packages/wallet-account/test/wallet_account.test.js @@ -1,9 +1,10 @@ const { KeyPair, PublicKey } = require('@near-js/crypto'); const { InMemoryKeyStore } = require('@near-js/keystores'); const { InMemorySigner } = require('@near-js/signers'); -const { actionCreators, createTransaction, SCHEMA, Transaction } = require('@near-js/transactions'); +const { baseDecode } = require('@near-js/utils'); +const { actionCreators, createTransaction, SCHEMA } = require('@near-js/transactions'); const BN = require('bn.js'); -const { baseDecode, deserialize } = require('borsh'); +const { deserialize } = require('borsh'); const localStorage = require('localstorage-memory'); const url = require('url'); @@ -260,8 +261,7 @@ function parseTransactionsFromUrl(urlToParse, callbackUrl = 'http://example.com/ }); const transactions = parsedUrl.query.transactions.split(',') .map(txBase64 => deserialize( - SCHEMA, - Transaction, + SCHEMA.Transaction, Buffer.from(txBase64, 'base64'))); return transactions; }