UNPKG

@massalabs/wallet-provider

Version:
226 lines 9.54 kB
import { getRequest, postRequest } from './RequestHandler'; import { walletApiUrl, MASSA_STATION_URL } from './MassaStationWallet'; import { argsToBase64, base64ToByteArray, uint8ArrayToBase64, } from '../utils/base64'; import { errorHandler } from '../errors/utils/errorHandler'; import { operationType } from '../utils/constants'; import { formatNodeStatusObject, Mas, MAX_GAS_CALL, Operation, SmartContract, strToBytes, StorageCost, formatMas, } from '@massalabs/massa-web3'; import { getClient, networkInfos } from './utils/network'; import { WalletName } from '../wallet'; import bs58check from 'bs58check'; /** * This module contains the MassaStationAccount class. It is responsible for representing an account in * the MassaStation wallet. * * @remarks * This class provides methods to interact with MassaStation account's {@link balance} and to {@link sign} messages. * */ export class MassaStationAccount { address; accountName; _providerName = WalletName.MassaWallet; constructor(address, accountName) { this.address = address; this.accountName = accountName; } get providerName() { return this._providerName; } async balance(final = false) { const { result } = await getRequest(`${MASSA_STATION_URL}massa/addresses?attributes=balance&addresses=${this.address}`); const balances = result.addressesAttributes[this.address].balance; return Mas.fromString(final ? balances.final : balances.pending); } async balanceOf(addresses, final = false) { const queryParams = new URLSearchParams(); queryParams.append('attributes', 'balance'); addresses.forEach((address) => { queryParams.append('addresses', address); }); const { result } = await getRequest(`${MASSA_STATION_URL}massa/addresses?${queryParams.toString()}`); return addresses.map((address) => { const balance = result.addressesAttributes[address].balance; return { address, balance: Mas.fromString(final ? balance.final : balance.pending), }; }); } async sign(data, opts) { const signData = { description: opts?.description ?? '', message: typeof data === 'string' ? data : new TextDecoder().decode(data), DisplayData: opts?.displayData ?? true, }; try { const { result } = await postRequest(`${walletApiUrl()}/accounts/${this.accountName}/signMessage`, signData); // MS Wallet encodes signature in base64... so we need to decode it en re-encode it in base58 const signature = bs58check.encode(await base64ToByteArray(result.signature)); return { publicKey: result.publicKey, signature, }; } catch (error) { throw errorHandler(operationType.Sign, error); } } async minimalFee() { const client = await getClient(); return client.getMinimalFee(); } async rollOperation(type, amount, opts) { let fee = opts?.fee ?? (await this.minimalFee()); const body = { fee: fee.toString(), amount: amount.toString(), side: type === operationType.BuyRolls ? 'buy' : 'sell', }; try { const { result } = await postRequest(`${walletApiUrl()}/accounts/${this.accountName}/rolls`, body); return new Operation(this, result.operationId); } catch (error) { throw errorHandler(type, error); } } async buyRolls(amount, opts) { return this.rollOperation(operationType.BuyRolls, amount, opts); } async sellRolls(amount, opts) { return this.rollOperation(operationType.SellRolls, amount, opts); } async transfer(to, amount, opts) { const fee = opts?.fee ?? (await this.minimalFee()); const body = { fee: fee.toString(), amount: amount.toString(), recipientAddress: to.toString(), }; try { const { result } = await postRequest(`${walletApiUrl()}/accounts/${this.accountName}/transfer`, body); return new Operation(this, result.operationId); } catch (error) { throw errorHandler(operationType.SendTransaction, error); } } async callSC(params) { // convert parameter to base64 let args = ''; if (params.parameter) { if (params.parameter instanceof Uint8Array) { args = uint8ArrayToBase64(params.parameter); } else { args = argsToBase64(params.parameter); } } let fee = params?.fee ?? (await this.minimalFee()); const body = { nickname: this.accountName, name: params.func, at: params.target, args, coins: params.coins ? Number(params.coins) : 0, fee: fee.toString(), // If maxGas is not provided, estimation will be done by MS maxGas: params.maxGas ? params.maxGas.toString() : '', async: true, }; try { const { result } = await postRequest(`${MASSA_STATION_URL}cmd/executeFunction`, body); return new Operation(this, result.operationId); } catch (error) { throw errorHandler('callSmartContract', error); } } async networkInfos() { return networkInfos(); } async readSC(params) { // Gas amount check if (params.maxGas && params.maxGas > MAX_GAS_CALL) { throw new Error(` The gas submitted ${params.maxGas.toString()} exceeds the max. allowed block gas of ${MAX_GAS_CALL.toString()} `); } const client = await getClient(); const fee = params?.fee ?? (await this.minimalFee()); const caller = params.caller ?? this.address; const args = params.parameter ?? new Uint8Array(); const readOnlyParams = { ...params, caller, fee, parameter: args instanceof Uint8Array ? args : Uint8Array.from(args.serialize()), }; // This implementation is wrong. We should use massaStation instead of targeting the node directly. return client.executeReadOnlyCall(readOnlyParams); } async deploySC(params) { try { const args = params.parameter ?? new Uint8Array(); const parameters = args instanceof Uint8Array ? args : args.serialize(); const coins = params.coins ?? 0n; // If coins is undefined, some vesions of station will have a panic const maxCoins = params.maxCoins ?? StorageCost.smartContractDeploy(params.byteCode.length) + coins; const fee = params.fee || (await this.minimalFee()); const body = { nickname: this.accountName, smartContract: uint8ArrayToBase64(params.byteCode), maxCoins: maxCoins.toString(), coins: coins.toString(), fee: fee.toString(), parameters: uint8ArrayToBase64(parameters), description: `${formatMas(coins)} $MAS coins allocated to datastore + ${formatMas(fee)} $MAS fee for operation`, }; const { result } = await postRequest(`${MASSA_STATION_URL}cmd/deploySC`, body); const operationId = result?.operationId; if (!operationId) throw new Error('Operation ID not found'); const operation = new Operation(this, operationId); const deployedAddress = await operation.getDeployedAddress(params.waitFinalExecution); return new SmartContract(this, deployedAddress); } catch (error) { throw new Error(`Error during smart contract deployment: ${error}`); } } executeSC() { throw new Error('Method not implemented.'); } async getOperationStatus(opId) { const client = await getClient(); // This implementation is wrong. We should use massaStation instead of targeting the node directly. return client.getOperationStatus(opId); } async getEvents(filter) { const client = await getClient(); // This implementation is wrong. We should use massaStation instead of targeting the node directly. return client.getEvents(filter); } async getNodeStatus() { const client = await getClient(); const status = await client.status(); return formatNodeStatusObject(status); } async getStorageKeys(address, filter = new Uint8Array(), final = true) { // This implementation is wrong. We should use massaStation instead of targeting the node directly. const client = await getClient(); const filterBytes = typeof filter === 'string' ? strToBytes(filter) : filter; return client.getDataStoreKeys(address, filterBytes, final); } async readStorage(address, keys, final = true) { // This implementation is wrong. We should use massaStation instead of targeting the node directly. const client = await getClient(); const entries = keys.map((key) => ({ key, address, })); return client.getDatastoreEntries(entries, final); } } //# sourceMappingURL=MassaStationAccount.js.map