UNPKG

@velas/account-agent

Version:

sdk

1,097 lines (941 loc) 63.9 kB
import bs58 from 'bs58'; import BN from "bn.js"; import AccountConstructor from '../helper/account'; import assert from '../helper/assert'; import ErrorHendler from '../helper/error'; import ScopesConstructor from '../helper/scopes'; import httpProvider from '../helper/http-provider'; import { SolidInstruction, SignerType } from '../helper/instructions' import { addConsoleHandler } from 'selenium-webdriver/lib/logging'; const errorHendler = new ErrorHendler('Client'); /** * Creates a new API Client to blockchain * @constructor * @param {Object} options */ function Client(options, keyStorage) { this.keyStorage = keyStorage; this.connection = new options.client_provider.Connection(options.client_host, 'singleGossip'); this.broadcast = options.broadcastTransactionHendler; this.web3 = options.client_provider; this.account_contract = options.client_account_contract; this.transactions_sponsor_pub_key = options.transactions_sponsor_pub_key; this.sc = new ScopesConstructor({ account_contract: options.client_account_contract }); this.accountDataConstructor = new AccountConstructor({ connection: this.connection, account_contract: options.client_account_contract, PublicKey: this.web3.PublicKey, }); }; Client.prototype.evmRawTransactionToNativeTransaction = httpProvider.evmTransaction Client.prototype.isSecret = function(secret) { try { const account = new this.web3.Account(typeof secret === "string" ? bs58.decode(secret) : secret); return true } catch (e) { return false } }; Client.prototype.isPubKey = function(pubkey) { try { const pubKey = new this.web3.PublicKey(pubkey); return true } catch (e) { return false } }; Client.prototype.signAndBroadcastWithKey = async function({ account, op_key, message, csrf_token, host, }) { assert.check(account, { type: 'string', message: 'account option is required' }); assert.check(op_key, { type: 'string', message: 'op_key option is required' }); assert.check(message, { type: 'object', message: 'message option is required' }); assert.check(csrf_token, { type: 'string', message: 'csrf_token option is required' }); assert.check(host, { type: 'string', message: 'host option is required' }); const transaction = this.web3.Transaction.populate(this.web3.Message.from(message), []); const signature = await this.keyStorage.signWithKey(op_key, message); const signatures = [[op_key, bs58.encode(signature)]]; const { error, transactions_signatures } = await this.broadcast(host, { transactions: [{ account, message: bs58.encode(transaction.serializeMessage()), signatures, }], csrf_token }); if (error) throw new Error(error); return transactions_signatures[0]; }; Client.prototype.signAndSendWithKey = async function({ account, op_key, message, }) { assert.check(account, { type: 'string', message: 'account option is required' }); assert.check(op_key, { type: 'string', message: 'op_key option is required' }); assert.check(message, { type: 'object', message: 'message option is required' }); const transaction = this.web3.Transaction.populate(this.web3.Message.from(message), []); const signature = await this.keyStorage.signWithKey(op_key, message); transaction.feePayer = new this.web3.PublicKey(op_key); transaction.addSignature(new this.web3.PublicKey(op_key), signature); if (!transaction.verifySignatures()) throw new Error(`Signature verification failed! Signer must be only one and it must be current session key: ${op_key}.`); return await this.web3.sendAndConfirmRawTransaction( this.connection, transaction.serialize(), { commitment: 'single', skipPreflight: true, }, ); }; Client.prototype.findAccountAddressWithPublicKey = async function(publicKey, return_base58 = true) { const ownerPublicKey = typeof publicKey === 'string' ? new this.web3.PublicKey(publicKey) : publicKey; const vaccount = await this.web3.PublicKey.findProgramAddress( [ ownerPublicKey.toBuffer().slice(0, 32), Buffer.from("vaccount"), ], new this.web3.PublicKey(this.account_contract), ); if (return_base58) return vaccount[0].toBase58(); return vaccount[0]; }; Client.prototype.getAccountPrograms = function(accountData) { return this.accountDataConstructor.getAccountPrograms(accountData); }; Client.prototype.getCurrentAccountPrograms = async function(accountData, op_key, programAddress) { const result = []; const accountScopes = accountData.operational_keys[op_key].scopes || []; const allPrograms = await this.accountDataConstructor.getAccountPrograms(accountData); const bignum = new BN(new Uint8Array(accountData.operational_keys[op_key].external_programs_indices)); const length = bignum.bitLength(); const bits = Array(256 - length).fill("0").concat(bignum.toString(2).split('')); const externalProgramsIndexes = bits.reduce((a,b,i) => { if (i === 0) a = []; if (b === '1') a.push(i); return a; }, {}); accountScopes.forEach(scope => { result.push(`${programAddress}:${scope}`); }) externalProgramsIndexes.forEach(index => { result.push(allPrograms[index]) }) return result; } Client.prototype.checkKeyForValidity = async function(sessionPublicKey, accountData) { const foundAccountAddress = await this.findAccountAddressWithPublicKey(sessionPublicKey); if (accountData.ephemeral && foundAccountAddress === accountData.account_key) return true; const isOperationalKey = accountData.operational_keys[sessionPublicKey]; const isOwnerKey = accountData.owner_keys.includes(sessionPublicKey); if (isOwnerKey) return true; if (isOperationalKey && isOperationalKey.active) return true; return false; }; Client.prototype.getAccountData = function(base58PublicKey) { const vaccountPublicKey = new this.web3.PublicKey(base58PublicKey); return this.accountDataConstructor.getAccountData(vaccountPublicKey); }; Client.prototype.getClientData = async function(client_id) { try { if (!client_id) return undefined; const publickKey = new this.web3.PublicKey(client_id); const account = await this.connection.getParsedAccountInfo(publickKey); if (!account.value || account.value.data.program !== 'velas-relying-party') return undefined; return { client_name: account.value.data.parsed.related_program_data.name, client_uri: account.value.data.parsed.related_program_data.domain_name, redirect_uris: account.value.data.parsed.related_program_data.redirect_uri, icon_id: account.value.data.parsed.related_program_data.icon_cid } } catch (_) { return undefined; }; }; Client.prototype.getBalance = function(base58PublicKey) { const publickKey = new this.web3.PublicKey(base58PublicKey); return this.connection.getBalance(publickKey); }; Client.prototype.transactionStatus = function(signature) { return this.connection.getSignatureStatus(signature); }; Client.prototype.sendMessage = async function({ transaction_name, params, transactions_sponsor_api_host, transactions_sponsor_auth_params, csrf_token, cb, }) { try { assert.check(params, { type: 'object', message: 'params option is required' }); assert.check(transactions_sponsor_api_host, { type: 'string', message: 'transactions_sponsor_api_host option is required' }); assert.check(csrf_token, { type: 'string', message: 'csrf_token option is required' }); assert.check(transaction_name, { type: 'string', message: 'transaction_name option is required' }); assert.check(cb, { type: 'function', message: 'cb option is required' }); if (![ 'proxy', 'transfer', 'initializeTransaction', 'backupEphemeral', 'backupOwner', 'initializeEphemeral', 'addOwnerTransaction', 'replaceOwnerTransaction', 'addOperationalAddressTransaction', 'removeOperationalAddressTransaction', 'extendOperationalScopesTransaction', 'mergeOperationalKeysTransaction' ].includes(transaction_name)) throw new Error(`unsupported transaction_name param`); var expression = /^(http:\/\/|https:\/\/)+(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-:]*[A-Za-z0-9])$/; var regex = new RegExp(expression); if (!transactions_sponsor_api_host.match(regex)) throw new Error('wrong host address for transactions_sponsor_api_host param'); } catch(e) { return cb({ error: 'send_message_invalid_params', description: e.message || e, description: `SendMessage error: ${e.message || e}`, }); }; let message_data, transaction_id; try { message_data = await this[transaction_name](params); } catch(e) { return cb({ error: 'send_message_generation_data_error', description: `Preparing a transaction error: ${e.message || e}`, }); }; try { const { error, transactions_signatures } = await this.broadcast(transactions_sponsor_api_host, { ...message_data, csrf_token, transactions_sponsor_auth_params, }); if (error) { return cb({ error: 'send_message_transaction_failed', description: `Broadcast transaction error: ${error}`, }); }; if (!transactions_signatures) throw new Error('no transaction transactions_signatures from broadcast host'); transaction_id = transactions_signatures.pop(); } catch(e) { return cb({ error: 'send_message_broadcast_host_error', description: `Broadcast host error: ${e.message || e}`, }); }; let i = 17; await ( async () => { while (i > 0) { try { const status = await this.transactionStatus(transaction_id); if (status && status.value && status.value.confirmationStatus === 'finalized') { return cb(null, { transaction_id, account: message_data.account, }); }; i--; if (status && status.value && status.value.err) { throw new Error( `Transaction failed: ${status.value.err}. ${transaction_id}`); }; if (i === 0) throw new Error(`Transaction wasn't completed within this time. ${transaction_id}`); } catch(e) { return cb({ error: 'send_message_invalid_transaction_status', description: `Transaction status error: ${e.message || e}. ${transaction_id}`, }); }; await new Promise(resolve => setTimeout(resolve, 2000)); }; })(); }; Client.prototype.proxy = function (params) { return params; }; Client.prototype.prepareTransaction = async function ({ feePayer, vaccount, instructions, owSigner, // TO DO: remove after finish refactoring opSigner, // TO DO: remove after finish refactoring signers, }) { const { blockhash: recentBlockhash } = await this.connection.getRecentBlockhash(); const transaction = new this.web3.Transaction({ recentBlockhash, feePayer }).add(...instructions); let signatures = []; if (signers) { for (const signer of signers) { const signerPubKey = signer.publicKey.toBase58(); if (signer.type === 'account') { transaction.sign(signer.account); for (const item of transaction.signatures) { const signatureOPubKey = item.publicKey.toBase58(); if ((signerPubKey === signatureOPubKey) && item.signature) { console.log("[ SIGN TRANSACTION | secsignWithSecret ]: with key ", signatureOPubKey, "signature:", bs58.encode(item.signature)); signatures.push([signatureOPubKey, bs58.encode(item.signature)]); }; }; }; if (signer.type === 'publicKey') { const signature = await this.keyStorage.signWithKey(signerPubKey, transaction.serializeMessage()); console.log("[ SIGN TRANSACTION | signWithKey ]: with key ", signerPubKey, "signature:", bs58.encode(signature)); signatures.push([signerPubKey, bs58.encode(signature)]); }; }; console.log('[prepared transaction]', transaction) return { account: vaccount.toBase58(), message: bs58.encode(transaction.serializeMessage()), signatures, }; } else { // TO DO: remove after finish refactoring //owSigner validation to support ephemeral accounts const pubOwSigner = owSigner.filter(item => !item.secretKey); const accountOwSigner = owSigner.filter(item => !!item.secretKey); if (accountOwSigner.length) transaction.sign(...accountOwSigner); const ow_signatures = []; for (const ow of accountOwSigner) { const owPubKey = ow.publicKey.toBase58(); for (const item of transaction.signatures) { const signPubKey = item.publicKey.toBase58(); if ((owPubKey === signPubKey) && item.signature) { console.log("prepareTransaction: found owner signature for - ", owPubKey, bs58.encode(item.signature)) ow_signatures.push([signPubKey, bs58.encode(item.signature)]) } } } for (const ow of pubOwSigner) { const op_signature = await this.keyStorage.signWithKey(ow.publicKey.toBase58(), transaction.serializeMessage()); ow_signatures.push([ow.publicKey.toBase58(), bs58.encode(op_signature)]); } signatures = [...ow_signatures]; if (opSigner) { const op_signature = await this.keyStorage.signWithKey(opSigner.toBase58(), transaction.serializeMessage()); signatures.push([opSigner.toBase58(), bs58.encode(op_signature)]); }; return { account: vaccount.toBase58(), message: bs58.encode(transaction.serializeMessage()), signatures, }; }; }; Client.prototype.backupEphemeral = async function (params) { const { vaccount, signerObjectCurrent, signerObjectNew, operationalPublicKey, payerPublicKey, agentType, } = await this.transactionParamsValidator2(params, [ ['secret', 'findAccountWithSecret', 'vaccount'], ['secret', 'signer', 'signerObjectCurrent'], ['secret_new', 'new_owner_signer', 'signerObjectNew'], ['op_key', 'base58PubKey', 'operationalPublicKey'], ['transactions_sponsor_pub_key', 'base58PubKey', 'payerPublicKey'] ], 'backupEphemeral'); const accountData = await this.accountDataConstructor.getAccountData(vaccount, true); console.log("accountData", accountData); if (!accountData.ephemeral) throw new Error('Account ' + vaccount.toBase58() + ' already exists for specified key'); const initInstruction = new this.web3.TransactionInstruction({ programId: new this.web3.PublicKey(this.account_contract), keys: [ {pubkey: vaccount, isSigner: false, isWritable: true}, {pubkey: signerObjectCurrent.publicKey, isSigner: true, isWritable: false}, {pubkey: new this.web3.PublicKey(this.sc.SYSVAR_RENT_ADDRESS), isSigner: false, isWritable: false}, {pubkey: new this.web3.PublicKey(this.sc.SYSTEM_PROGRAM_ADDRESS), isSigner: false, isWritable: false}, ], data: accountData.instruction.initializeEncoded, }); const replaceOwnerInstruction = new this.web3.TransactionInstruction({ programId: new this.web3.PublicKey(this.account_contract), keys: [ { pubkey: vaccount, isSigner: false, isWritable: true }, { pubkey: signerObjectCurrent.publicKey, isSigner: false, isWritable: false }, { pubkey: signerObjectNew.publicKey, isSigner: true, isWritable: false }, { pubkey: signerObjectCurrent.publicKey, isSigner: true, isWritable: false }, ], data: accountData.instruction.replaceOwnerEncoded, }); // Generating a new operational key const addOperationalData = await this.accountDataConstructor.addOperationalData(accountData, { pubkey: operationalPublicKey, agentType: agentType, scopes: [`${this.sc.VELAS_ACCOUNT_PROGRAM_ADDRESS}:10`, `${this.sc.VELAS_ACCOUNT_PROGRAM_ADDRESS}:11`], }); const addOperationalAddressInstruction = new this.web3.TransactionInstruction({ programId: new this.web3.PublicKey(this.account_contract), keys: [ { pubkey: vaccount, isSigner: false, isWritable: true }, { pubkey: addOperationalData.storage.tokens_next, isSigner: false, isWritable: true }, { pubkey: addOperationalData.storage.programs_current, isSigner: false, isWritable: true }, { pubkey: accountData.storage.storage_current, isSigner: false, isWritable: true }, { pubkey: operationalPublicKey, isSigner: false, isWritable: false }, { pubkey: new this.web3.PublicKey(this.sc.SYSVAR_RENT_ADDRESS), isSigner: false, isWritable: false }, { pubkey: new this.web3.PublicKey(this.sc.SYSTEM_PROGRAM_ADDRESS), isSigner: false, isWritable: false }, { pubkey: signerObjectNew.publicKey, isSigner: true, isWritable: false }, ], data: addOperationalData.storage.addOperationalEncoded, }); const transferInstruction = this.web3.SystemProgram.transfer({ fromPubkey: payerPublicKey, toPubkey: vaccount, lamports: await this.connection.getMinimumBalanceForRentExemption(135) + addOperationalData.storage.rent }); return { transactions: [await this.prepareTransaction({ feePayer: payerPublicKey, vaccount, instructions: [transferInstruction, initInstruction, replaceOwnerInstruction, addOperationalAddressInstruction], signers: [signerObjectCurrent, signerObjectNew], })] }; } Client.prototype.backupOwner = async function (params) { const { vaccount, signerObjectCurrent, signerObjectNew, operationalPublicKey, payerPublicKey, agentType, } = await this.transactionParamsValidator2(params, [ ['secret', 'findAccountWithSecret', 'vaccount'], ['secret', 'signer', 'signerObjectCurrent'], ['secret_new', 'new_owner_signer', 'signerObjectNew'], ['op_key', 'base58PubKey', 'operationalPublicKey'], ['transactions_sponsor_pub_key', 'base58PubKey', 'payerPublicKey'] ], 'backupOwner'); const accountData = await this.accountDataConstructor.getAccountData(vaccount, true); const replaceOwnerInstruction = new this.web3.TransactionInstruction({ programId: new this.web3.PublicKey(this.account_contract), keys: [ { pubkey: vaccount, isSigner: false, isWritable: true }, { pubkey: signerObjectCurrent.publicKey, isSigner: false, isWritable: false }, { pubkey: signerObjectNew.publicKey, isSigner: true, isWritable: false }, { pubkey: signerObjectCurrent.publicKey, isSigner: true, isWritable: false }, ], data: accountData.instruction.replaceOwnerEncoded, }); // Generating a new operational key const addOperationalData = await this.accountDataConstructor.addOperationalData(accountData, { pubkey: operationalPublicKey, agentType: agentType, scopes: [`${this.sc.VELAS_ACCOUNT_PROGRAM_ADDRESS}:10`, `${this.sc.VELAS_ACCOUNT_PROGRAM_ADDRESS}:11`], }); const addOperationalAddressInstruction = new this.web3.TransactionInstruction({ programId: new this.web3.PublicKey(this.account_contract), keys: [ { pubkey: vaccount, isSigner: false, isWritable: true }, { pubkey: addOperationalData.storage.tokens_next, isSigner: false, isWritable: true }, { pubkey: addOperationalData.storage.programs_current, isSigner: false, isWritable: true }, { pubkey: accountData.storage.storage_current, isSigner: false, isWritable: true }, { pubkey: operationalPublicKey, isSigner: false, isWritable: false }, { pubkey: new this.web3.PublicKey(this.sc.SYSVAR_RENT_ADDRESS), isSigner: false, isWritable: false }, { pubkey: new this.web3.PublicKey(this.sc.SYSTEM_PROGRAM_ADDRESS), isSigner: false, isWritable: false }, { pubkey: signerObjectNew.publicKey, isSigner: true, isWritable: false }, ], data: addOperationalData.storage.addOperationalEncoded, }); const transferInstruction = this.web3.SystemProgram.transfer({ fromPubkey: payerPublicKey, toPubkey: vaccount, lamports: addOperationalData.storage.rent }); return { transactions: [await this.prepareTransaction({ feePayer: payerPublicKey, vaccount, instructions: [transferInstruction, replaceOwnerInstruction, addOperationalAddressInstruction], signers: [signerObjectCurrent, signerObjectNew], })] }; } Client.prototype.initializeEphemeral = async function (params) { const { vaccount, accountData, signerObject, payerPublicKey, } = await this.transactionParamsValidator2(params, [ ['secret', 'findAccountWithSecret', 'vaccount'], ['secret', 'signer', 'signerObject'], ], 'initializeEphemeral'); if (!accountData.ephemeral) throw new Error('Account ' + vaccount.toBase58() + ' already exists for specified key'); const transfer = this.web3.SystemProgram.transfer({ fromPubkey: payerPublicKey, toPubkey: vaccount, lamports: await this.connection.getMinimumBalanceForRentExemption(135) }); const init = new this.web3.TransactionInstruction({ programId: new this.web3.PublicKey(this.account_contract), keys: [ {pubkey: vaccount, isSigner: false, isWritable: true}, {pubkey: signerObject.publicKey, isSigner: true, isWritable: false}, {pubkey: new this.web3.PublicKey(this.sc.SYSVAR_RENT_ADDRESS), isSigner: false, isWritable: false}, {pubkey: new this.web3.PublicKey(this.sc.SYSTEM_PROGRAM_ADDRESS), isSigner: false, isWritable: false}, ], data: accountData.instruction.initializeEncoded, }); return { transactions: [await this.prepareTransaction({ feePayer: payerPublicKey, vaccount: vaccount, instructions: [transfer, init], signers: [signerObject], })] }; } Client.prototype.initializeTransaction = async function (params) { const { vaccount, accountData, signerObject, operationalPublicKey, payerPublicKey, agentType, scopes, } = await this.transactionParamsValidator2(params, [ ['secret', 'findAccountWithSecret', 'vaccount'], ['secret', 'signer', 'signerObject'], ['op_key', 'base58PubKey', 'operationalPublicKey'], ], 'initializeTransaction'); if (!accountData.ephemeral) throw new Error('Account ' + vaccount.toBase58() + ' already exists for specified key'); const addOperationalData = await this.accountDataConstructor.addOperationalData(accountData, { pubkey: operationalPublicKey, agentType: agentType, scopes: scopes, }); const transfer = this.web3.SystemProgram.transfer({ fromPubkey: payerPublicKey, toPubkey: vaccount, lamports: addOperationalData.storage.rent }); const init = new this.web3.TransactionInstruction({ programId: new this.web3.PublicKey(this.account_contract), keys: [ { pubkey: vaccount, isSigner: false, isWritable: true }, { pubkey: signerObject.publicKey, isSigner: true, isWritable: false }, { pubkey: new this.web3.PublicKey(this.sc.SYSVAR_RENT_ADDRESS), isSigner: false, isWritable: false }, { pubkey: new this.web3.PublicKey(this.sc.SYSTEM_PROGRAM_ADDRESS), isSigner: false, isWritable: false }, ], data: accountData.instruction.initializeEncoded, }); let addPrograms = []; for (const programObject of addOperationalData.storage.whitelistPrograms) { addPrograms.push(new this.web3.TransactionInstruction({ programId: new this.web3.PublicKey(this.account_contract), keys: [ { pubkey: vaccount, isSigner: false, isWritable: true }, { pubkey: accountData.storage.storage_current, isSigner: false, isWritable: true }, { pubkey: programObject.pubkey, isSigner: false, isWritable: false }, { pubkey: programObject.current, isSigner: false, isWritable: true }, //{ pubkey: programObject.next, isSigner: false, isWritable: true }, { pubkey: new this.web3.PublicKey(this.sc.SYSVAR_RENT_ADDRESS), isSigner: false, isWritable: false }, { pubkey: new this.web3.PublicKey(this.sc.SYSTEM_PROGRAM_ADDRESS), isSigner: false, isWritable: false }, { pubkey: signerObject.publicKey, isSigner: true, isWritable: false }, ], data: addOperationalData.storage.addProgramEncoded, })) }; const addOperationalAddressInstruction = new this.web3.TransactionInstruction({ programId: new this.web3.PublicKey(this.account_contract), keys: [ { pubkey: vaccount, isSigner: false, isWritable: true }, { pubkey: addOperationalData.storage.tokens_next, isSigner: false, isWritable: true }, { pubkey: addOperationalData.storage.programs_current, isSigner: false, isWritable: true }, { pubkey: accountData.storage.storage_current, isSigner: false, isWritable: true }, //{ pubkey: accountData.storage.storage_next, isSigner: false, isWritable: true }, { pubkey: operationalPublicKey, isSigner: false, isWritable: false }, { pubkey: new this.web3.PublicKey(this.sc.SYSVAR_RENT_ADDRESS), isSigner: false, isWritable: false }, { pubkey: new this.web3.PublicKey(this.sc.SYSTEM_PROGRAM_ADDRESS), isSigner: false, isWritable: false }, { pubkey: signerObject.publicKey, isSigner: true, isWritable: false }, ], data: addOperationalData.storage.addOperationalEncoded, }); return { transactions: [await this.prepareTransaction({ feePayer: payerPublicKey, vaccount: vaccount, instructions: [transfer, init, ...addPrograms, addOperationalAddressInstruction], signers: [signerObject], })] }; }; Client.prototype.transactionParamsValidator2 = async function (params = {}, required, action) { const response = {}; for (const req of required) { if (!params[req[0]] && !req[3]) errorHendler.error(`${req[0]} option is required to call ${action} method`, null); }; if (params.transactions_sponsor_pub_key) { try { let value = bs58.decode(params.transactions_sponsor_pub_key); value = new this.web3.PublicKey(value); response.payerPublicKey = new this.web3.PublicKey(params.transactions_sponsor_pub_key); } catch (error) { errorHendler.error(`${action}. Invalid parametr transactions_sponsor_pub_key: It should be base58 string with public key`, error); }; } else { params.payerPublicKey = new this.web3.PublicKey(this.transactions_sponsor_pub_key || this.sc.SYSTEM_PROGRAM_ADDRESS); }; if (params.agent_type) { if (typeof params.agent_type !== 'string') errorHendler.error(`Invalid parametr agent_type`, null); response.agentType = params.agent_type; } else { response.agentType = 'Incognito'; }; if (params.scopes) { response.scopes = params.scopes; }; for (const [param, type, name] of required) { let value = params[param]; if (!value) { continue }; switch(type) { case 'accountData': try { value = bs58.decode(value); value = new this.web3.PublicKey(value); response.accountData = await this.accountDataConstructor.getAccountData(value, true); } catch (error) { errorHendler.error(`Invalid parametr ${param} (${action}): It should be base58 string with public key`, error); } break; case 'findAccountWithSecret': try { value = typeof value === 'string' ? bs58.decode(value) : value; let isPubKey = false; try { isPubKey = new this.web3.PublicKey(params.secret) } catch (_) {}; const account = !isPubKey ? new this.web3.Account(value) : { publicKey: isPubKey }; value = await this.findAccountAddressWithPublicKey(account.publicKey, false); response.accountData = await this.accountDataConstructor.getAccountData(value, true); response[name] = value; } catch (error) { errorHendler.error(`Invalid parametr ${param} (${action}): It should be base58 string with secret or public key`, error); }; break; case 'secret': try { value = typeof value === 'string' ? bs58.decode(value) : value; let isPubKey = false; try { isPubKey = new this.web3.PublicKey(params.secret) } catch (_) {}; response[name] = !isPubKey ? new this.web3.Account(value) : { publicKey: isPubKey, }; } catch (error) { errorHendler.error(`Invalid parametr ${param} (${action}): It should be base58 string with secret or public key`, error); }; break; case 'lamports': response[name] = value; break; case 'base58PubKey': try { value = bs58.decode(value); response[name] = new this.web3.PublicKey(value); } catch (error) { errorHendler.error(`Invalid parametr ${param} (${action}): It should be base58 string with public key`, error); } break; case 'signer': try { value = bs58.decode(value); } catch (error) { errorHendler.error(`Invalid parametr ${param} (${action}): It should be base58 string with public key or secret`, error); }; if (!response.accountData) errorHendler.error(`${action}: getting accountData is required before ${param})`, null); let account; try { account = new this.web3.Account(value) } catch (_) {}; if (account) { const foundAccountAddress = await this.findAccountAddressWithPublicKey(account.publicKey); if (response.accountData.ephemeral) { if (foundAccountAddress === response.accountData.account_key) { response[name] = { ephemeral: true, type: 'account', account: account, publicKey: account.publicKey, signerType: SignerType.owner(), } } else { errorHendler.error(`${action}. Invalid parametr ${param}: specified key can't be as a signer for ephemeral account ${response.accountData.account_key}`, null); }; } else { const key = account.publicKey.toBase58(); const isOperationalKey = response.accountData.operational_keys[key]; const isOwnerKey = response.accountData.owner_keys.includes(key); if (isOwnerKey) { response[name] = { type: 'account', account: account, publicKey: account.publicKey, signerType: SignerType.owner(), } } else if (isOperationalKey) { response[name] = { type: 'account', account: account, publicKey: account.publicKey, publicKeyIndex: isOperationalKey.index, signerType: SignerType.operational(isOperationalKey.index), } } else { response[name] = { type: 'account', account: account, publicKey: account.publicKey, signerType: SignerType.owner(), } //errorHendler.error(`${action}. Invalid parametr ${param}: specified key can't be as a signer for account ${response.accountData.account_key}`, null); }; }; } else { let publicKeyObject; try { publicKeyObject = new this.web3.PublicKey(value)} catch (_) { console.log("PUBKEY", _) }; if (publicKeyObject) { const key = publicKeyObject.toBase58(); const isOperationalKey = response.accountData.operational_keys ? response.accountData.operational_keys[key] : null; const isOwnerKey = response.accountData.owner_keys ? response.accountData.owner_keys.includes(key) : null; if (isOwnerKey || response.accountData.ephemeral) { response[name] = { type: 'publicKey', publicKey: publicKeyObject, signerType: SignerType.owner(), } } else if (isOperationalKey) { response[name] = { type: 'publicKey', publicKey: publicKeyObject, publicKeyIndex: isOperationalKey.index, signerType: SignerType.operational(isOperationalKey.index), } } else { errorHendler.error(`${action}. Invalid parametr ${param}: specified key can't be as a signer for account ${response.accountData.account_key}`, null); }; } else { errorHendler.error(`Invalid parametr ${param} (${action}): It should be base58 string with public key or secret`, error); }; }; break; case 'new_owner_signer': try { value = bs58.decode(value); } catch (error) { errorHendler.error(`Invalid parametr ${param} (${action}): It should be base58 string with public key or secret`, error); } let accountNew; try { accountNew = new this.web3.Account(value) } catch (_) {}; if (accountNew) { response[name] = { type: 'account', account: accountNew, publicKey: accountNew.publicKey, signerType: SignerType.owner(), } } else { let publicKeyObject; try { publicKeyObject = new this.web3.PublicKey(value)} catch (_) { console.log("PUBKEY", _) }; if (publicKeyObject) { response[name] = { type: 'publicKey', publicKey: publicKeyObject, signerType: SignerType.owner(), } } else { errorHendler.error(`Invalid parametr ${param} (${action}): It should be base58 string with public key or secret`, error); } } break; default: errorHendler.error(`Not supported type (${action}): ${type}`, null); }; }; return response; }; Client.prototype.addOperationalAddressTransaction = async function (params) { const { accountData, velasAccountPubKey, newOperationalPubKey, signerObject, payerPublicKey, agentType, scopes, } = await this.transactionParamsValidator2(params, [ ['velas_account', 'accountData', 'accountData'], ['velas_account', 'base58PubKey', 'velasAccountPubKey'], ['new_operational_public_key', 'base58PubKey', 'newOperationalPubKey'], ['transaction_signer', 'signer', 'signerObject'], ], 'addOperationalAddressTransaction'); if (!accountData || accountData.ephemeral) throw new Error("Specified account not found"); const addOperationalData = await this.accountDataConstructor.addOperationalData(accountData, { pubkey: newOperationalPubKey, agentType: agentType, scopes: scopes, }, signerObject.signerType); console.log("addOperationalData ==>", addOperationalData) let instructions = []; if (addOperationalData.storage.rent > 0) { instructions.push(this.web3.SystemProgram.transfer({ fromPubkey: payerPublicKey, toPubkey: velasAccountPubKey, lamports: addOperationalData.storage.rent })); }; for (const programObject of addOperationalData.storage.whitelistPrograms) { console.log("==> need add program =>", programObject) instructions.push(new this.web3.TransactionInstruction({ programId: new this.web3.PublicKey(this.account_contract), keys: [ { pubkey: velasAccountPubKey, isSigner: false, isWritable: true }, { pubkey: accountData.storage.storage_current, isSigner: false, isWritable: true }, { pubkey: programObject.pubkey, isSigner: false, isWritable: false }, { pubkey: programObject.current, isSigner: false, isWritable: true }, //{ pubkey: programObject.next, isSigner: false, isWritable: true }, { pubkey: new this.web3.PublicKey(this.sc.SYSVAR_RENT_ADDRESS), isSigner: false, isWritable: false }, { pubkey: new this.web3.PublicKey(this.sc.SYSTEM_PROGRAM_ADDRESS), isSigner: false, isWritable: false }, { pubkey: signerObject.publicKey, isSigner: true, isWritable: false }, ], data: addOperationalData.storage.addProgramEncoded, })) }; const addOperationalAddressInstruction = new this.web3.TransactionInstruction({ programId: new this.web3.PublicKey(this.account_contract), keys: [ { pubkey: velasAccountPubKey, isSigner: false, isWritable: true }, { pubkey: addOperationalData.storage.tokens_next, isSigner: false, isWritable: true }, { pubkey: addOperationalData.storage.programs_current, isSigner: false, isWritable: true }, { pubkey: accountData.storage.storage_current, isSigner: false, isWritable: true }, //{ pubkey: accountData.storage.storage_next, isSigner: false, isWritable: true }, { pubkey: newOperationalPubKey, isSigner: false, isWritable: false }, { pubkey: new this.web3.PublicKey(this.sc.SYSVAR_RENT_ADDRESS), isSigner: false, isWritable: false }, { pubkey: new this.web3.PublicKey(this.sc.SYSTEM_PROGRAM_ADDRESS), isSigner: false, isWritable: false }, { pubkey: signerObject.publicKey, isSigner: true, isWritable: false }, ], data: addOperationalData.storage.addOperationalEncoded, }); return { transactions: [await this.prepareTransaction({ feePayer: payerPublicKey, vaccount: velasAccountPubKey, instructions: [...instructions, addOperationalAddressInstruction], signers: [signerObject], })] }; }; Client.prototype.transfer = async function(params) { const { accountData, velasAccountPubKey, toPubKey, signerObject, lamports, } = await this.transactionParamsValidator2(params, [ ['fromAccountPubKey', 'accountData', 'accountData'], ['fromAccountPubKey', 'base58PubKey', 'velasAccountPubKey'], ['toPubKey', 'base58PubKey', 'toPubKey'], ['ownerOrOprationalPubKey', 'signer', 'signerObject'], ['lamports', 'lamports', 'lamports'], ], 'transfer'); const transfer = new this.web3.TransactionInstruction({ programId: new this.web3.PublicKey(this.account_contract), keys: [ { pubkey: velasAccountPubKey, isSigner: false, isWritable: true }, { pubkey: accountData.storage.storage_current, isSigner: false, isWritable: true }, { pubkey: toPubKey, isSigner: false, isWritable: true }, { pubkey: new this.web3.PublicKey(this.sc.SYSVAR_RENT_ADDRESS), isSigner: false, isWritable: false }, { pubkey: new this.web3.PublicKey(this.sc.SYSTEM_PROGRAM_ADDRESS), isSigner: false, isWritable: false }, { pubkey: signerObject.publicKey, isSigner: true, isWritable: false }, ], data: SolidInstruction.transfer({ amount: lamports, signerType: signerObject.signerType }).encode(), }); return new this.web3.Transaction().add(transfer); }; Client.prototype.replaceOwnerTransaction = async function(params) { const { accountData, velasAccountPubKey, currentOwnerPubKey, newOwnerPubKey, payerPublicKey, signerObjectCurrent, signerObjectNew, } = await this.transactionParamsValidator2(params, [ ['velas_account', 'accountData', 'accountData'], ['velas_account', 'base58PubKey', 'velasAccountPubKey'], ['current_owner_public_key', 'base58PubKey', 'currentOwnerPubKey'], ['new_owner_public_key', 'base58PubKey', 'newOwnerPubKey'], ['current_owner_transaction_signer', 'signer', 'signerObjectCurrent', true], ['new_owner_transaction_signer', 'signer', 'signerObjectNew', true], ], 'replaceOwnerTransaction'); if (!accountData || accountData.ephemeral) throw new Error("Specified account not found"); if (!accountData.owner_keys.includes(currentOwnerPubKey.toBase58())) throw new Error("You dont have rights to specified account"); const replaceOwnerInstruction = new this.web3.TransactionInstruction({ programId: new this.web3.PublicKey(this.account_contract), keys: [ { pubkey: velasAccountPubKey, isSigner: false, isWritable: true }, { pubkey: currentOwnerPubKey, isSigner: false, isWritable: false }, { pubkey: newOwnerPubKey, isSigner: true, isWritable: false }, { pubkey: currentOwnerPubKey, isSigner: true, isWritable: false }, ], data: accountData.instruction.replaceOwnerEncoded, }); const signers = []; if (signerObjectCurrent) signers.push(signerObjectCurrent); if (signerObjectNew) signers.push(signerObjectNew); return { transactions: [await this.prepareTransaction({ feePayer: payerPublicKey, vaccount: velasAccountPubKey, instructions: [replaceOwnerInstruction], signers, })] }; }; Client.prototype.removeOperationalAddressTransaction = async function(params) { const { accountData, velasAccountPubKey, signerObject, signerObjectToRemove, payerPublicKey } = await this.transactionParamsValidator2(params, [ ['account', 'accountData', 'accountData'], ['account', 'base58PubKey', 'velasAccountPubKey'], ['ownerOrOperationalToSignTx', 'signer', 'signerObject'], ['publicKeyOperationalToRemove', 'signer', 'signerObjectToRemove'], ], 'transfer'); if (!accountData) throw new Error("Specified account not found"); const removeOperationalData = await this.accountDataConstructor.removeOperationalData(accountData, signerObjectToRemove.publicKey.toBase58()); let removePrograms = []; for (const permissionToRemoveIndex of removeOperationalData.storage.programPermissionsToRemove) { removePrograms.push(new this.web3.TransactionInstruction({ programId: new this.web3.PublicKey(this.account_contract), keys: [ { pubkey: velasAccountPubKey, isSigner: false, isWritable: true }, { pubkey: accountData.storage.storage_current, isSigner: false, isWritable: true }, { pubkey: removeOperationalData.storage.programs_current, isSigner: false, isWritable: true }, { pubkey: signerObject.publicKey, isSigner: true, isWritable: false }, ], data: SolidInstruction.removeProgramPermission({ operational2RemoveIndex: signerObjectToRemove.publicKeyIndex, signerType: signerObject.signerType, program2RemoveIndex: permissionToRemoveIndex, }).encode(), })); }; const removeOperationalAddressInstruction = new this.web3.TransactionInstruction({ programId: new this.web3.PublicKey(this.account_contract), keys: [ { pubkey: velasAccountPubKey, isSigner: false, isWritable: true }, { pubkey: accountData.storage.storage_current, isSigner: false, isWritable: true }, //{ pubkey: accountData.storage.storage_ne