@velas/account-agent
Version:
sdk
1,097 lines (941 loc) • 63.9 kB
JavaScript
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