near-api-js
Advanced tools
Comparing version 0.44.2 to 0.45.0
@@ -15,2 +15,20 @@ import BN from 'bn.js'; | ||
declare type verifyCodeFunction = (securityCode: any) => Promise<any>; | ||
export declare enum MultisigDeleteRequestRejectionError { | ||
CANNOT_DESERIALIZE_STATE = "Cannot deserialize the contract state", | ||
MULTISIG_NOT_INITIALIZED = "Smart contract panicked: Multisig contract should be initialized before usage", | ||
NO_SUCH_REQUEST = "Smart contract panicked: panicked at 'No such request: either wrong number or already confirmed'", | ||
REQUEST_COOLDOWN_ERROR = "Request cannot be deleted immediately after creation.", | ||
METHOD_NOT_FOUND = "Contract method is not found" | ||
} | ||
export declare enum MultisigStateStatus { | ||
INVALID_STATE = 0, | ||
STATE_NOT_INITIALIZED = 1, | ||
VALID_STATE = 2, | ||
UNKNOWN_STATE = 3 | ||
} | ||
declare enum MultisigCodeStatus { | ||
INVALID_CODE = 0, | ||
VALID_CODE = 1, | ||
UNKNOWN_CODE = 2 | ||
} | ||
export declare class AccountMultisig extends Account { | ||
@@ -23,4 +41,10 @@ storage: any; | ||
private _signAndSendTransaction; | ||
checkMultisigCodeAndStateStatus(contractBytes?: Uint8Array): Promise<{ | ||
codeStatus: MultisigCodeStatus; | ||
stateStatus: MultisigStateStatus; | ||
}>; | ||
deleteRequest(request_id: any): Promise<FinalExecutionOutcome>; | ||
deleteAllRequests(): Promise<void>; | ||
deleteUnconfirmedRequests(): Promise<void>; | ||
getRequestIds(): Promise<string>; | ||
getRequestIds(): Promise<string[]>; | ||
getRequest(): any; | ||
@@ -58,3 +82,14 @@ setRequest(data: any): any; | ||
deployMultisig(contractBytes: Uint8Array): Promise<FinalExecutionOutcome>; | ||
disable(contractBytes: Uint8Array): Promise<FinalExecutionOutcome>; | ||
disableWithFAK({ contractBytes, cleanupContractBytes }: { | ||
contractBytes: Uint8Array; | ||
cleanupContractBytes?: Uint8Array; | ||
}): Promise<FinalExecutionOutcome>; | ||
get2faDisableCleanupActions(cleanupContractBytes: Uint8Array): Promise<Action[]>; | ||
get2faDisableKeyConversionActions(): Promise<Action[]>; | ||
/** | ||
* This method converts LAKs back to FAKs, clears state and deploys an 'empty' contract (contractBytes param) | ||
* @param [contractBytes]{@link https://github.com/near/near-wallet/blob/master/packages/frontend/src/wasm/main.wasm?raw=true} | ||
* @param [cleanupContractBytes]{@link https://github.com/near/core-contracts/blob/master/state-cleanup/res/state_cleanup.wasm?raw=true} | ||
*/ | ||
disable(contractBytes: Uint8Array, cleanupContractBytes: Uint8Array): Promise<FinalExecutionOutcome>; | ||
sendCodeDefault(): Promise<any>; | ||
@@ -61,0 +96,0 @@ getCodeDefault(method: any): Promise<string>; |
@@ -6,3 +6,3 @@ 'use strict'; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Account2FA = exports.AccountMultisig = exports.MULTISIG_CONFIRM_METHODS = exports.MULTISIG_CHANGE_METHODS = exports.MULTISIG_DEPOSIT = exports.MULTISIG_GAS = exports.MULTISIG_ALLOWANCE = exports.MULTISIG_STORAGE_KEY = void 0; | ||
exports.Account2FA = exports.AccountMultisig = exports.MultisigStateStatus = exports.MultisigDeleteRequestRejectionError = exports.MULTISIG_CONFIRM_METHODS = exports.MULTISIG_CHANGE_METHODS = exports.MULTISIG_DEPOSIT = exports.MULTISIG_GAS = exports.MULTISIG_ALLOWANCE = exports.MULTISIG_STORAGE_KEY = void 0; | ||
const bn_js_1 = __importDefault(require("bn.js")); | ||
@@ -14,2 +14,3 @@ const depd_1 = __importDefault(require("depd")); | ||
const transaction_1 = require("./transaction"); | ||
const providers_1 = require("./providers"); | ||
const web_1 = require("./utils/web"); | ||
@@ -23,2 +24,24 @@ exports.MULTISIG_STORAGE_KEY = '__multisigRequest'; | ||
exports.MULTISIG_CONFIRM_METHODS = ['confirm']; | ||
var MultisigDeleteRequestRejectionError; | ||
(function (MultisigDeleteRequestRejectionError) { | ||
MultisigDeleteRequestRejectionError["CANNOT_DESERIALIZE_STATE"] = "Cannot deserialize the contract state"; | ||
MultisigDeleteRequestRejectionError["MULTISIG_NOT_INITIALIZED"] = "Smart contract panicked: Multisig contract should be initialized before usage"; | ||
MultisigDeleteRequestRejectionError["NO_SUCH_REQUEST"] = "Smart contract panicked: panicked at 'No such request: either wrong number or already confirmed'"; | ||
MultisigDeleteRequestRejectionError["REQUEST_COOLDOWN_ERROR"] = "Request cannot be deleted immediately after creation."; | ||
MultisigDeleteRequestRejectionError["METHOD_NOT_FOUND"] = "Contract method is not found"; | ||
})(MultisigDeleteRequestRejectionError = exports.MultisigDeleteRequestRejectionError || (exports.MultisigDeleteRequestRejectionError = {})); | ||
; | ||
var MultisigStateStatus; | ||
(function (MultisigStateStatus) { | ||
MultisigStateStatus[MultisigStateStatus["INVALID_STATE"] = 0] = "INVALID_STATE"; | ||
MultisigStateStatus[MultisigStateStatus["STATE_NOT_INITIALIZED"] = 1] = "STATE_NOT_INITIALIZED"; | ||
MultisigStateStatus[MultisigStateStatus["VALID_STATE"] = 2] = "VALID_STATE"; | ||
MultisigStateStatus[MultisigStateStatus["UNKNOWN_STATE"] = 3] = "UNKNOWN_STATE"; | ||
})(MultisigStateStatus = exports.MultisigStateStatus || (exports.MultisigStateStatus = {})); | ||
var MultisigCodeStatus; | ||
(function (MultisigCodeStatus) { | ||
MultisigCodeStatus[MultisigCodeStatus["INVALID_CODE"] = 0] = "INVALID_CODE"; | ||
MultisigCodeStatus[MultisigCodeStatus["VALID_CODE"] = 1] = "VALID_CODE"; | ||
MultisigCodeStatus[MultisigCodeStatus["UNKNOWN_CODE"] = 2] = "UNKNOWN_CODE"; | ||
})(MultisigCodeStatus || (MultisigCodeStatus = {})); | ||
// in memory request cache for node w/o localStorage | ||
@@ -87,2 +110,53 @@ const storageFallback = { | ||
} | ||
/* | ||
* This method submits a canary transaction that is expected to always fail in order to determine whether the contract currently has valid multisig state | ||
* and whether it is initialized. The canary transaction attempts to delete a request at index u32_max and will go through if a request exists at that index. | ||
* a u32_max + 1 and -1 value cannot be used for the canary due to expected u32 error thrown before deserialization attempt. | ||
*/ | ||
async checkMultisigCodeAndStateStatus(contractBytes) { | ||
const u32_max = 4294967295; | ||
const validCodeStatusIfNoDeploy = contractBytes ? MultisigCodeStatus.UNKNOWN_CODE : MultisigCodeStatus.VALID_CODE; | ||
try { | ||
if (contractBytes) { | ||
await super.signAndSendTransaction({ | ||
receiverId: this.accountId, actions: [ | ||
transaction_1.deployContract(contractBytes), | ||
transaction_1.functionCall('delete_request', { request_id: u32_max }, exports.MULTISIG_GAS, exports.MULTISIG_DEPOSIT) | ||
] | ||
}); | ||
} | ||
else { | ||
await this.deleteRequest(u32_max); | ||
} | ||
return { codeStatus: MultisigCodeStatus.VALID_CODE, stateStatus: MultisigStateStatus.VALID_STATE }; | ||
} | ||
catch (e) { | ||
if (new RegExp(MultisigDeleteRequestRejectionError.CANNOT_DESERIALIZE_STATE).test(e && e.kind && e.kind.ExecutionError)) { | ||
return { codeStatus: validCodeStatusIfNoDeploy, stateStatus: MultisigStateStatus.INVALID_STATE }; | ||
} | ||
else if (new RegExp(MultisigDeleteRequestRejectionError.MULTISIG_NOT_INITIALIZED).test(e && e.kind && e.kind.ExecutionError)) { | ||
return { codeStatus: validCodeStatusIfNoDeploy, stateStatus: MultisigStateStatus.STATE_NOT_INITIALIZED }; | ||
} | ||
else if (new RegExp(MultisigDeleteRequestRejectionError.NO_SUCH_REQUEST).test(e && e.kind && e.kind.ExecutionError)) { | ||
return { codeStatus: validCodeStatusIfNoDeploy, stateStatus: MultisigStateStatus.VALID_STATE }; | ||
} | ||
else if (new RegExp(MultisigDeleteRequestRejectionError.METHOD_NOT_FOUND).test(e && e.message)) { | ||
// not reachable if transaction included a deploy | ||
return { codeStatus: MultisigCodeStatus.INVALID_CODE, stateStatus: MultisigStateStatus.UNKNOWN_STATE }; | ||
} | ||
throw e; | ||
} | ||
} | ||
deleteRequest(request_id) { | ||
return super.signAndSendTransaction({ | ||
receiverId: this.accountId, | ||
actions: [transaction_1.functionCall('delete_request', { request_id }, exports.MULTISIG_GAS, exports.MULTISIG_DEPOSIT)] | ||
}); | ||
} | ||
async deleteAllRequests() { | ||
const request_ids = await this.getRequestIds(); | ||
if (request_ids.length) { | ||
await Promise.all(request_ids.map((id) => this.deleteRequest(id))); | ||
} | ||
} | ||
async deleteUnconfirmedRequests() { | ||
@@ -177,9 +251,51 @@ // TODO: Delete in batch, don't delete unexpired | ||
]; | ||
if ((await this.state()).code_hash === '11111111111111111111111111111111') { | ||
actions.push(transaction_1.functionCall('new', newArgs, exports.MULTISIG_GAS, exports.MULTISIG_DEPOSIT)); | ||
const newFunctionCallActionBatch = actions.concat(transaction_1.functionCall('new', newArgs, exports.MULTISIG_GAS, exports.MULTISIG_DEPOSIT)); | ||
console.log('deploying multisig contract for', accountId); | ||
const { stateStatus: multisigStateStatus } = await this.checkMultisigCodeAndStateStatus(contractBytes); | ||
switch (multisigStateStatus) { | ||
case MultisigStateStatus.STATE_NOT_INITIALIZED: | ||
return await super.signAndSendTransactionWithAccount(accountId, newFunctionCallActionBatch); | ||
case MultisigStateStatus.VALID_STATE: | ||
return await super.signAndSendTransactionWithAccount(accountId, actions); | ||
case MultisigStateStatus.INVALID_STATE: | ||
throw new providers_1.TypedError(`Can not deploy a contract to account ${this.accountId} on network ${this.connection.networkId}, the account has existing state.`, 'ContractHasExistingState'); | ||
default: | ||
throw new providers_1.TypedError(`Can not deploy a contract to account ${this.accountId} on network ${this.connection.networkId}, the account state could not be verified.`, 'ContractStateUnknown'); | ||
} | ||
console.log('deploying multisig contract for', accountId); | ||
return await super.signAndSendTransactionWithAccount(accountId, actions); | ||
} | ||
async disable(contractBytes) { | ||
async disableWithFAK({ contractBytes, cleanupContractBytes }) { | ||
let cleanupActions = []; | ||
if (cleanupContractBytes) { | ||
await this.deleteAllRequests().catch(e => e); | ||
cleanupActions = await this.get2faDisableCleanupActions(cleanupContractBytes); | ||
} | ||
const keyConversionActions = await this.get2faDisableKeyConversionActions(); | ||
const actions = [ | ||
...cleanupActions, | ||
...keyConversionActions, | ||
transaction_1.deployContract(contractBytes) | ||
]; | ||
const accessKeyInfo = await this.findAccessKey(this.accountId, actions); | ||
if (accessKeyInfo && accessKeyInfo.accessKey && accessKeyInfo.accessKey.permission !== 'FullAccess') { | ||
throw new providers_1.TypedError(`No full access key found in keystore. Unable to bypass multisig`, 'NoFAKFound'); | ||
} | ||
return this.signAndSendTransactionWithAccount(this.accountId, actions); | ||
} | ||
async get2faDisableCleanupActions(cleanupContractBytes) { | ||
const currentAccountState = await this.viewState('').catch(error => { | ||
const cause = error.cause && error.cause.name; | ||
if (cause == 'NO_CONTRACT_CODE') { | ||
return []; | ||
} | ||
throw cause == 'TOO_LARGE_CONTRACT_STATE' | ||
? new providers_1.TypedError(`Can not deploy a contract to account ${this.accountId} on network ${this.connection.networkId}, the account has existing state.`, 'ContractHasExistingState') | ||
: error; | ||
}); | ||
const currentAccountStateKeys = currentAccountState.map(({ key }) => key.toString('base64')); | ||
return currentAccountState.length ? [ | ||
transaction_1.deployContract(cleanupContractBytes), | ||
transaction_1.functionCall('clean', { keys: currentAccountStateKeys }, exports.MULTISIG_GAS, new bn_js_1.default('0')) | ||
] : []; | ||
} | ||
async get2faDisableKeyConversionActions() { | ||
const { accountId } = this; | ||
@@ -196,11 +312,34 @@ const accessKeys = await this.getAccessKeys(); | ||
const confirmOnlyKey = key_pair_1.PublicKey.from((await this.postSignedJson('/2fa/getAccessKey', { accountId })).publicKey); | ||
const actions = [ | ||
return [ | ||
transaction_1.deleteKey(confirmOnlyKey), | ||
...lak2fak.map(({ public_key }) => transaction_1.deleteKey(key_pair_1.PublicKey.from(public_key))), | ||
...lak2fak.map(({ public_key }) => transaction_1.addKey(key_pair_1.PublicKey.from(public_key), null)), | ||
...lak2fak.map(({ public_key }) => transaction_1.addKey(key_pair_1.PublicKey.from(public_key), transaction_1.fullAccessKey())) | ||
]; | ||
} | ||
/** | ||
* This method converts LAKs back to FAKs, clears state and deploys an 'empty' contract (contractBytes param) | ||
* @param [contractBytes]{@link https://github.com/near/near-wallet/blob/master/packages/frontend/src/wasm/main.wasm?raw=true} | ||
* @param [cleanupContractBytes]{@link https://github.com/near/core-contracts/blob/master/state-cleanup/res/state_cleanup.wasm?raw=true} | ||
*/ | ||
async disable(contractBytes, cleanupContractBytes) { | ||
const { stateStatus } = await this.checkMultisigCodeAndStateStatus(); | ||
if (stateStatus !== MultisigStateStatus.VALID_STATE && stateStatus !== MultisigStateStatus.STATE_NOT_INITIALIZED) { | ||
throw new providers_1.TypedError(`Can not deploy a contract to account ${this.accountId} on network ${this.connection.networkId}, the account state could not be verified.`, 'ContractStateUnknown'); | ||
} | ||
let deleteAllRequestsError; | ||
await this.deleteAllRequests().catch(e => deleteAllRequestsError = e); | ||
const cleanupActions = await this.get2faDisableCleanupActions(cleanupContractBytes).catch(e => { | ||
if (e.type === 'ContractHasExistingState') { | ||
throw deleteAllRequestsError || e; | ||
} | ||
throw e; | ||
}); | ||
const actions = [ | ||
...cleanupActions, | ||
...(await this.get2faDisableKeyConversionActions()), | ||
transaction_1.deployContract(contractBytes), | ||
]; | ||
console.log('disabling 2fa for', accountId); | ||
console.log('disabling 2fa for', this.accountId); | ||
return await this.signAndSendTransaction({ | ||
receiverId: accountId, | ||
receiverId: this.accountId, | ||
actions | ||
@@ -207,0 +346,0 @@ }); |
@@ -68,2 +68,6 @@ /// <reference types="node" /> | ||
stringify?: (input: any) => Buffer; | ||
/** | ||
* Is contract from JS SDK, automatically encodes args from JS SDK to binary. | ||
*/ | ||
jsContract?: boolean; | ||
} | ||
@@ -70,0 +74,0 @@ declare function parseJsonFromRawResponse(response: Uint8Array): any; |
@@ -283,8 +283,20 @@ "use strict"; | ||
} | ||
functionCallV2({ contractId, methodName, args = {}, gas = constants_1.DEFAULT_FUNCTION_CALL_GAS, attachedDeposit, walletMeta, walletCallbackUrl, stringify }) { | ||
functionCallV2({ contractId, methodName, args = {}, gas = constants_1.DEFAULT_FUNCTION_CALL_GAS, attachedDeposit, walletMeta, walletCallbackUrl, stringify, jsContract }) { | ||
this.validateArgs(args); | ||
const stringifyArg = stringify === undefined ? transaction_1.stringifyJsonOrBytes : stringify; | ||
let functionCallArgs; | ||
if (jsContract) { | ||
function encodeCall(contractId, method, args) { | ||
return Buffer.concat([Buffer.from(contractId), Buffer.from([0]), Buffer.from(method), Buffer.from([0]), Buffer.from(args)]); | ||
} | ||
const encodedArgs = encodeCall(contractId, methodName, JSON.stringify(Object.values(args))); | ||
functionCallArgs = ['call_js_contract', encodedArgs, gas, attachedDeposit, null, true]; | ||
} | ||
else { | ||
const stringifyArg = stringify === undefined ? transaction_1.stringifyJsonOrBytes : stringify; | ||
functionCallArgs = [methodName, args, gas, attachedDeposit, stringifyArg, false]; | ||
} | ||
return this.signAndSendTransaction({ | ||
receiverId: contractId, | ||
actions: [transaction_1.functionCall(methodName, args, gas, attachedDeposit, stringifyArg)], | ||
receiverId: jsContract ? this.connection.jsvmAccountId : contractId, | ||
// eslint-disable-next-line prefer-spread | ||
actions: [transaction_1.functionCall.apply(void 0, functionCallArgs)], | ||
walletMeta, | ||
@@ -291,0 +303,0 @@ walletCallbackUrl |
@@ -10,2 +10,3 @@ import { Provider } from './providers'; | ||
readonly signer: Signer; | ||
readonly jsvmAccountId: string; | ||
constructor(networkId: string, provider: Provider, signer: Signer); | ||
@@ -12,0 +13,0 @@ /** |
@@ -63,2 +63,6 @@ /** | ||
walletUrl?: string; | ||
/** | ||
* JVSM account ID for NEAR JS SDK | ||
*/ | ||
jsvmAccountId?: string; | ||
} | ||
@@ -65,0 +69,0 @@ /** |
@@ -34,3 +34,4 @@ "use strict"; | ||
provider: { type: 'JsonRpcProvider', args: { url: config.nodeUrl, headers: config.headers } }, | ||
signer: config.signer || { type: 'InMemorySigner', keyStore: config.keyStore || config.deps.keyStore } | ||
signer: config.signer || { type: 'InMemorySigner', keyStore: config.keyStore || (config.deps && config.deps.keyStore) }, | ||
jsvmAccountId: config.jsvmAccountId || `jsvm.${config.networkId}` | ||
}); | ||
@@ -37,0 +38,0 @@ if (config.masterAccount) { |
@@ -66,3 +66,3 @@ /// <reference types="node" /> | ||
*/ | ||
export declare function functionCall(methodName: string, args: Uint8Array | object, gas: BN, deposit: BN, stringify?: typeof stringifyJsonOrBytes): Action; | ||
export declare function functionCall(methodName: string, args: Uint8Array | object, gas: BN, deposit: BN, stringify?: typeof stringifyJsonOrBytes, jsContract?: boolean): Action; | ||
export declare function transfer(deposit: BN): Action; | ||
@@ -69,0 +69,0 @@ export declare function stake(stake: BN, publicKey: PublicKey): Action; |
@@ -82,3 +82,6 @@ "use strict"; | ||
*/ | ||
function functionCall(methodName, args, gas, deposit, stringify = stringifyJsonOrBytes) { | ||
function functionCall(methodName, args, gas, deposit, stringify = stringifyJsonOrBytes, jsContract = false) { | ||
if (jsContract) { | ||
return new Action({ functionCall: new FunctionCall({ methodName, args, gas, deposit }) }); | ||
} | ||
return new Action({ functionCall: new FunctionCall({ methodName, args: stringify(args), gas, deposit }) }); | ||
@@ -85,0 +88,0 @@ } |
{ | ||
"name": "near-api-js", | ||
"description": "JavaScript library to interact with NEAR Protocol via RPC API", | ||
"version": "0.44.2", | ||
"version": "0.45.0", | ||
"repository": { | ||
@@ -15,3 +15,3 @@ "type": "git", | ||
"bn.js": "5.2.0", | ||
"borsh": "^0.6.0", | ||
"borsh": "^0.7.0", | ||
"bs58": "^4.0.0", | ||
@@ -66,3 +66,4 @@ "depd": "^2.0.0", | ||
"prefuzz": "yarn build", | ||
"fuzz": "jsfuzz test/fuzz/borsh-roundtrip.js test/fuzz/corpus/" | ||
"fuzz": "jsfuzz test/fuzz/borsh-roundtrip.js test/fuzz/corpus/", | ||
"prepare": "husky install" | ||
}, | ||
@@ -69,0 +70,0 @@ "bundlewatch": { |
@@ -57,3 +57,3 @@ # near-api-js | ||
1. [Change hash for the commit with errors in the nearcore](https://github.com/near/near-api-js/blob/master/gen_error_types.js#L7-L9) | ||
1. [Change hash for the commit with errors in the nearcore](https://github.com/near/near-api-js/blob/master/fetch_error_schema.js#L8-L9) | ||
2. Fetch new schema: `node fetch_error_schema.js` | ||
@@ -60,0 +60,0 @@ 3. `yarn build` to update `lib/**.js` files |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1075621
84
23643
77
1
16
1
31
13
+ Addedborsh@0.7.0(transitive)
- Removedborsh@0.6.0(transitive)
Updatedborsh@^0.7.0