near-workspaces
Version:
Write tests in TypeScript/JavaScript to run in a controlled NEAR Sandbox local environment.
295 lines • 12.4 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Account = void 0;
const buffer_1 = require("buffer");
const process_1 = require("process");
const borsh = __importStar(require("borsh"));
const types_1 = require("../types");
const contract_state_1 = require("../contract-state");
const jsonrpc_1 = require("../jsonrpc");
const utils_1 = require("../utils");
const transaction_result_1 = require("../transaction-result");
const record_1 = require("../record");
class Account {
_accountId;
manager;
constructor(_accountId, manager) {
this._accountId = _accountId;
this.manager = manager;
}
async accountView() {
return this.manager.accountView(this._accountId);
}
async exists() {
return this.provider.accountExists(this.accountId);
}
get provider() {
return this.manager.provider;
}
get accountId() {
return this._accountId;
}
async availableBalance() {
return this.manager.availableBalance(this.accountId);
}
async balance() {
return this.manager.balance(this.accountId);
}
batch(receiver) {
return this.manager.batch(this, receiver);
}
async getKey() {
return this.manager.getKey(this.accountId);
}
async setKey(keyPair) {
const keyPairResult = await this.manager.setKey(this.accountId, keyPair);
return keyPairResult.getPublicKey();
}
async createAccount(accountId, { keyPair, initialBalance, } = {}) {
const tx = await this.internalCreateAccount(accountId, {
keyPair,
initialBalance,
isSubAccount: false,
});
const result = await tx.transact();
if (result.Failure) {
throw new Error(`Failure during trasaction excecution, details: ${JSON.stringify(result)}`);
}
return this.getAccount(accountId);
}
async createSubAccount(accountId, { keyPair, initialBalance, } = {}) {
const tx = await this.internalCreateAccount(accountId, {
keyPair,
initialBalance,
isSubAccount: true,
});
const result = await tx.transact();
if (result.Failure) {
throw new Error(`Failure during trasaction excecution, details: ${JSON.stringify(result)}`);
}
return this.getSubAccount(accountId);
}
async importContract({ testnetContract, mainnetContract, withData = false, blockId, keyPair, initialBalance, }) {
if ((testnetContract && mainnetContract) ?? !(testnetContract ?? mainnetContract)) {
throw new TypeError('Provide `mainnetContract` or `testnetContract` but not both.');
}
const network = mainnetContract ? 'mainnet' : 'testnet';
const refContract = (mainnetContract ?? testnetContract);
const rpc = jsonrpc_1.JsonRpcProvider.fromNetwork(network);
const blockQuery = blockId ? { block_id: blockId } : undefined;
const account = this.getAccount(refContract);
// Get account view of account on reference network
const accountView = await rpc.viewAccount(refContract, blockQuery);
accountView.amount = initialBalance?.toString() ?? accountView.amount;
const pubKey = await account.setKey(keyPair);
const records = account.recordBuilder()
.account(accountView)
.accessKey(pubKey);
if (accountView.code_hash !== utils_1.EMPTY_CONTRACT_HASH) {
const binary = await rpc.viewCodeRaw(refContract, blockQuery);
records.contract(binary);
}
await account.patchStateRecords(records);
if (!await this.provider.accountExists(refContract)) {
await account.patchStateRecords(records);
if (!await this.provider.accountExists(refContract)) {
throw new Error(`Account ${refContract} does not exist after trying to patch into sandbox.`);
}
}
if (withData) {
const rawData = await rpc.viewStateRaw(account.accountId, '', blockQuery);
const data = rawData.map(({ key, value }) => ({
Data: {
account_id: account.accountId, data_key: key, value,
},
}));
await account.patchStateRecords({ records: data });
}
return account;
}
getSubAccount(accountId) {
const id = this.makeSubAccount(accountId);
return this.getAccount(id);
}
getAccount(accountId) {
return new Account(accountId, this.manager);
}
async deploy(code) {
const tx = await this.batch(this).deployContractFile(code);
return tx.transact();
}
async devCreateAccount({ initialBalance, keyPair, } = {}) {
const accountId = `${(0, utils_1.randomAccountId)('dev-', 5, 5)}.${this.accountId}`;
const tx = await this.internalCreateAccount(accountId, {
keyPair,
initialBalance,
});
const result = await tx.transact();
if (result.Failure) {
throw new Error(`Failure during account creation, details: ${JSON.stringify(result)}`);
}
return this.getAccount(accountId);
}
async devDeploy(wasm, { attachedDeposit = utils_1.NO_DEPOSIT, args = {}, gas = types_1.DEFAULT_FUNCTION_CALL_GAS, initialBalance, keyPair, method, isSubAccount, } = {}) {
const accountId = `${(0, utils_1.randomAccountId)('dev-', 5, 5)}.${this.accountId}`;
let tx = await this.internalCreateAccount(accountId, {
keyPair,
initialBalance,
isSubAccount,
});
tx = await tx.deployContractFile(wasm);
if (method) {
tx.functionCall(method, args, { gas, attachedDeposit });
}
const result = await tx.transact();
if (result.Failure) {
throw new Error(`Failure during trasaction excecution, details: ${JSON.stringify(result)}`);
}
return this.getAccount(accountId);
}
async callRaw(contractId, methodName, args, { gas = types_1.DEFAULT_FUNCTION_CALL_GAS, attachedDeposit = utils_1.NO_DEPOSIT, signWithKey = undefined, } = {}) {
return this.batch(contractId)
.functionCall(methodName, args, { gas, attachedDeposit })
.transact(signWithKey);
}
async call(contractId, methodName, args, { gas = types_1.DEFAULT_FUNCTION_CALL_GAS, attachedDeposit = utils_1.NO_DEPOSIT, signWithKey = undefined, } = {}) {
const txResult = await this.callRaw(contractId, methodName, args, {
gas,
attachedDeposit,
signWithKey,
});
if (!process_1.env.NEAR_WORKSPACES_NO_LOGS && txResult.logs.length > 0) {
const accountId = typeof contractId === 'string' ? contractId : contractId.accountId;
console.log(`Contract logs from ${accountId}.${methodName}(${JSON.stringify(args)}) call:`, txResult.logs);
}
if (txResult.failed) {
throw new transaction_result_1.TransactionError(txResult);
}
return txResult.parseResult();
}
async viewRaw(method, args = {}) {
return this.provider.viewCall(this.accountId, method, args);
}
async view(method, args = {}) {
const result = await this.viewRaw(method, args);
if (!process_1.env.NEAR_WORKSPACES_NO_LOGS && result.logs.length > 0) {
console.log(`Contract logs from ${this.accountId}.${method}(${JSON.stringify(args)}) view call:`, result.logs);
}
if (result.result) {
const value = buffer_1.Buffer.from(result.result).toString();
try {
return JSON.parse(value);
}
catch {
return value;
}
}
return null;
}
async viewCode() {
return this.provider.viewCode(this.accountId);
}
async viewCodeRaw() {
return this.provider.viewCodeRaw(this.accountId);
}
async viewState(prefix = '') {
return new contract_state_1.ContractState(await this.provider.viewState(this.accountId, prefix));
}
async viewStateRaw(prefix = '') {
return this.provider.viewStateRaw(this.accountId, prefix);
}
async viewAccessKey(accountId, publicKey) {
return this.provider.viewAccessKey(accountId, publicKey);
}
async viewAccessKeys(accountId) {
return this.provider.viewAccessKeys(accountId);
}
async patchState(key, value_, borshSchema) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
return this.updateData(buffer_1.Buffer.from(key), buffer_1.Buffer.from(borshSchema ? borsh.serialize(borshSchema, value_) : value_));
}
async patchStateRecords(records) {
// FIX THIS: Shouldn't need two calls to update before next RPC view call.
await this.provider.patchStateRecords(records);
return this.provider.patchStateRecords(records);
}
async delete(beneficiaryId, keyPair) {
const result = await this.batch(this)
.deleteAccount(beneficiaryId)
.transact(keyPair);
if (result.succeeded && await this.getKey() !== null) {
await this.manager.deleteKey(this.accountId);
}
return result;
}
makeSubAccount(accountId) {
return `${accountId}.${this.accountId}`;
}
subAccountOf(accountId) {
return accountId.endsWith(`.${this.accountId}`);
}
toJSON() {
return this.accountId;
}
async updateAccount(accountData) {
return this.patchStateRecords(this.recordBuilder().account(accountData));
}
async updateAccessKey(key, access_key_data) {
return this.patchStateRecords(this.recordBuilder().accessKey(key, access_key_data));
}
async updateContract(binary) {
const accountView = await this.accountView();
const rb = this.recordBuilder();
rb.account(accountView);
return this.patchStateRecords(rb.contract(binary));
}
async updateData(key, value) {
const keyString = key instanceof buffer_1.Buffer ? key.toString('base64') : key;
const valueString = value instanceof buffer_1.Buffer ? value.toString('base64') : value;
return this.patchStateRecords(this.recordBuilder().data(keyString, valueString));
}
async transfer(accountId, amount) {
return this.batch(accountId).transfer(amount).transact();
}
async internalCreateAccount(accountId, { keyPair, initialBalance, isSubAccount, } = {}) {
const newAccountId = isSubAccount ? this.makeSubAccount(accountId) : accountId;
const keyPairResult = await this.getOrCreateKey(newAccountId, keyPair);
const pubKey = keyPairResult.getPublicKey();
const amount = (initialBalance ?? this.manager.initialBalance);
return this.batch(newAccountId)
.createAccount()
.transfer(amount)
.addKey(pubKey);
}
async getOrCreateKey(accountId, keyPair) {
return (await this.manager.getKey(accountId)) ?? this.manager.setKey(accountId, keyPair);
}
recordBuilder() {
return record_1.RecordBuilder.fromAccount(this);
}
}
exports.Account = Account;
//# sourceMappingURL=account.js.map