@near-js/accounts
Version:
Classes encapsulating account-specific functionality
348 lines (347 loc) • 12.8 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var runtime_exports = {};
__export(runtime_exports, {
Runtime: () => Runtime
});
module.exports = __toCommonJS(runtime_exports);
var import_sha256 = require("@noble/hashes/sha256");
const notImplemented = (name) => () => {
throw new Error("method not implemented: " + name);
};
const prohibitedInView = (name) => () => {
throw new Error("method not available for view calls: " + name);
};
class Runtime {
context;
wasm;
memory;
registers;
logs;
result;
constructor({ contractCode, ...context }) {
this.context = context;
this.wasm = this.prepareWASM(Buffer.from(contractCode, "base64"));
this.memory = new WebAssembly.Memory({ initial: 1024, maximum: 2048 });
this.registers = {};
this.logs = [];
this.result = Buffer.from([]);
}
readUTF16CStr(ptr) {
const arr = [];
const mem = new Uint16Array(this.memory.buffer);
let key = Number(ptr) / 2;
while (mem[key] != 0) {
arr.push(mem[key]);
key++;
}
return Buffer.from(Uint16Array.from(arr).buffer).toString("ucs2");
}
readUTF8CStr(len, ptr) {
const arr = [];
const mem = new Uint8Array(this.memory.buffer);
let key = Number(ptr);
for (let i = 0; i < len && mem[key] != 0; i++) {
arr.push(mem[key]);
key++;
}
return Buffer.from(arr).toString("utf8");
}
storageRead(keyLen, keyPtr) {
const storageKey = Buffer.from(new Uint8Array(this.memory.buffer, Number(keyPtr), Number(keyLen)));
const stateVal = this.context.contractState.filter((obj) => Buffer.compare(obj.key, storageKey) === 0).map((obj) => obj.value);
if (stateVal.length === 0) return null;
return stateVal.length > 1 ? stateVal : stateVal[0];
}
prepareWASM(input) {
const parts = [];
const magic = input.subarray(0, 4);
if (magic.toString("utf8") !== "\0asm") {
throw new Error("Invalid magic number");
}
const version = input.readUInt32LE(4);
if (version != 1) {
throw new Error("Invalid version: " + version);
}
let offset = 8;
parts.push(input.subarray(0, offset));
function decodeLEB128() {
let result = 0;
let shift = 0;
let byte;
do {
byte = input[offset++];
result |= (byte & 127) << shift;
shift += 7;
} while (byte & 128);
return result;
}
function decodeLimits() {
const flags = input[offset++];
const hasMax = flags & 1;
const initial = decodeLEB128();
const max = hasMax ? decodeLEB128() : null;
return { initial, max };
}
function decodeString() {
const length = decodeLEB128();
const result = input.subarray(offset, offset + length);
offset += length;
return result.toString("utf8");
}
function encodeLEB128(value) {
const result = [];
do {
let byte = value & 127;
value >>= 7;
if (value !== 0) {
byte |= 128;
}
result.push(byte);
} while (value !== 0);
return Buffer.from(result);
}
function encodeString(value) {
const result = Buffer.from(value, "utf8");
return Buffer.concat([encodeLEB128(result.length), result]);
}
do {
const sectionStart = offset;
const sectionId = input.readUInt8(offset);
offset++;
const sectionSize = decodeLEB128();
const sectionEnd = offset + sectionSize;
if (sectionId == 5) {
parts.push(Buffer.from([5, 1, 0]));
} else if (sectionId == 2) {
const sectionParts = [];
const numImports = decodeLEB128();
for (let i = 0; i < numImports; i++) {
const importStart = offset;
decodeString();
decodeString();
const kind = input.readUInt8(offset);
offset++;
let skipImport = false;
switch (kind) {
case 0:
decodeLEB128();
break;
case 1:
offset++;
decodeLimits();
break;
case 2:
decodeLimits();
skipImport = true;
break;
case 3:
offset++;
offset++;
break;
default:
throw new Error("Invalid import kind: " + kind);
}
if (!skipImport) {
sectionParts.push(input.subarray(importStart, offset));
}
}
const importMemory = Buffer.concat([
encodeString("env"),
encodeString("memory"),
Buffer.from([2]),
// Memory import
Buffer.from([0]),
encodeLEB128(1)
]);
sectionParts.push(importMemory);
const sectionData = Buffer.concat([
encodeLEB128(sectionParts.length),
...sectionParts
]);
parts.push(Buffer.concat([
Buffer.from([2]),
// Import section
encodeLEB128(sectionData.length),
sectionData
]));
} else if (sectionId == 7) {
const sectionParts = [];
const numExports = decodeLEB128();
for (let i = 0; i < numExports; i++) {
const exportStart = offset;
decodeString();
const kind = input.readUInt8(offset);
offset++;
decodeLEB128();
if (kind !== 2) {
sectionParts.push(input.subarray(exportStart, offset));
}
}
const sectionData = Buffer.concat([
encodeLEB128(sectionParts.length),
...sectionParts
]);
parts.push(Buffer.concat([
Buffer.from([7]),
// Export section
encodeLEB128(sectionData.length),
sectionData
]));
} else {
parts.push(input.subarray(sectionStart, sectionEnd));
}
offset = sectionEnd;
} while (offset < input.length);
return Buffer.concat(parts);
}
// Host functions
getRegisterLength(registerId) {
return BigInt(this.registers[registerId.toString()] ? this.registers[registerId.toString()].length : Number.MAX_SAFE_INTEGER);
}
readFromRegister(registerId, ptr) {
const mem = new Uint8Array(this.memory.buffer);
mem.set(this.registers[registerId.toString()] || Buffer.from([]), Number(ptr));
}
getCurrentAccountId(registerId) {
this.registers[registerId.toString()] = Buffer.from(this.context.contractId);
}
inputMethodArgs(registerId) {
this.registers[registerId.toString()] = Buffer.from(this.context.methodArgs);
}
getBlockHeight() {
return BigInt(this.context.blockHeight);
}
getBlockTimestamp() {
return BigInt(this.context.blockTimestamp);
}
sha256(valueLen, valuePtr, registerId) {
const value = new Uint8Array(this.memory.buffer, Number(valuePtr), Number(valueLen));
this.registers[registerId.toString()] = (0, import_sha256.sha256)(value);
}
returnValue(valueLen, valuePtr) {
this.result = Buffer.from(new Uint8Array(this.memory.buffer, Number(valuePtr), Number(valueLen)));
}
panic(message) {
throw new Error("panic: " + message);
}
abort(msg_ptr, filename_ptr, line, col) {
const msg = this.readUTF16CStr(msg_ptr);
const filename = this.readUTF16CStr(filename_ptr);
const message = `${msg} ${filename}:${line}:${col}`;
if (!msg || !filename) {
throw new Error("abort: String encoding is bad UTF-16 sequence.");
}
throw new Error("abort: " + message);
}
appendToLog(len, ptr) {
this.logs.push(this.readUTF8CStr(len, ptr));
}
readStorage(key_len, key_ptr, register_id) {
const result = this.storageRead(key_len, key_ptr);
if (result == null) {
return 0n;
}
this.registers[register_id] = result;
return 1n;
}
hasStorageKey(key_len, key_ptr) {
const result = this.storageRead(key_len, key_ptr);
if (result == null) {
return 0n;
}
return 1n;
}
getHostImports() {
return {
register_len: this.getRegisterLength.bind(this),
read_register: this.readFromRegister.bind(this),
current_account_id: this.getCurrentAccountId.bind(this),
input: this.inputMethodArgs.bind(this),
block_index: this.getBlockHeight.bind(this),
block_timestamp: this.getBlockTimestamp.bind(this),
sha256: this.sha256.bind(this),
value_return: this.returnValue.bind(this),
abort: this.abort.bind(this),
log_utf8: this.appendToLog.bind(this),
log_utf16: this.appendToLog.bind(this),
storage_read: this.readStorage.bind(this),
storage_has_key: this.hasStorageKey.bind(this),
panic: () => this.panic("explicit guest panic"),
panic_utf8: (len, ptr) => this.panic(this.readUTF8CStr(len, ptr)),
// Not implemented
epoch_height: notImplemented("epoch_height"),
storage_usage: notImplemented("storage_usage"),
account_balance: notImplemented("account_balance"),
account_locked_balance: notImplemented("account_locked_balance"),
random_seed: notImplemented("random_seed"),
ripemd160: notImplemented("ripemd160"),
keccak256: notImplemented("keccak256"),
keccak512: notImplemented("keccak512"),
ecrecover: notImplemented("ecrecover"),
validator_stake: notImplemented("validator_stake"),
validator_total_stake: notImplemented("validator_total_stake"),
// Prohibited
write_register: prohibitedInView("write_register"),
signer_account_id: prohibitedInView("signer_account_id"),
signer_account_pk: prohibitedInView("signer_account_pk"),
predecessor_account_id: prohibitedInView("predecessor_account_id"),
attached_deposit: prohibitedInView("attached_deposit"),
prepaid_gas: prohibitedInView("prepaid_gas"),
used_gas: prohibitedInView("used_gas"),
promise_create: prohibitedInView("promise_create"),
promise_then: prohibitedInView("promise_then"),
promise_and: prohibitedInView("promise_and"),
promise_batch_create: prohibitedInView("promise_batch_create"),
promise_batch_then: prohibitedInView("promise_batch_then"),
promise_batch_action_create_account: prohibitedInView("promise_batch_action_create_account"),
promise_batch_action_deploy_contract: prohibitedInView("promise_batch_action_deploy_contract"),
promise_batch_action_function_call: prohibitedInView("promise_batch_action_function_call"),
promise_batch_action_function_call_weight: prohibitedInView("promise_batch_action_function_call_weight"),
promise_batch_action_transfer: prohibitedInView("promise_batch_action_transfer"),
promise_batch_action_stake: prohibitedInView("promise_batch_action_stake"),
promise_batch_action_add_key_with_full_access: prohibitedInView("promise_batch_action_add_key_with_full_access"),
promise_batch_action_add_key_with_function_call: prohibitedInView("promise_batch_action_add_key_with_function_call"),
promise_batch_action_delete_key: prohibitedInView("promise_batch_action_delete_key"),
promise_batch_action_delete_account: prohibitedInView("promise_batch_action_delete_account"),
promise_results_count: prohibitedInView("promise_results_count"),
promise_result: prohibitedInView("promise_result"),
promise_return: prohibitedInView("promise_return"),
storage_write: prohibitedInView("storage_write"),
storage_remove: prohibitedInView("storage_remove")
};
}
async execute(methodName) {
const module2 = await WebAssembly.compile(this.wasm);
const instance = await WebAssembly.instantiate(module2, { env: { ...this.getHostImports(), memory: this.memory } });
const callMethod = instance.exports[methodName];
if (callMethod == void 0) {
throw new Error(`Contract method '${methodName}' does not exists in contract ${this.context.contractId} for block id ${this.context.blockHeight}`);
}
callMethod();
return {
result: this.result,
logs: this.logs
};
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Runtime
});