PHP WebShell
Текущая директория: /opt/BitGoJS/modules/sdk-coin-sol/test/unit
Просмотр файла: utils.ts
import should from 'should';
import { Utils } from '../../src/lib';
import * as testData from '../resources/sol';
import { Lockup, PublicKey, StakeProgram, SystemProgram, TransactionInstruction } from '@solana/web3.js';
import {
MEMO_PROGRAM_PK,
stakingActivateInstructionsIndexes,
stakingDeactivateInstructionsIndexes,
stakingWithdrawInstructionsIndexes,
} from '../../src/lib/constants';
import BigNumber from 'bignumber.js';
describe('SOL util library', function () {
describe('isValidAddress', function () {
it('should fail to validate invalid addresses', function () {
for (const address of testData.addresses.invalidAddresses) {
should.doesNotThrow(() => Utils.isValidAddress(address));
should.equal(Utils.isValidAddress(address), false);
}
// @ts-expect-error Testing for missing param, should not throw an error
should.doesNotThrow(() => Utils.isValidAddress(undefined));
// @ts-expect-error Testing for missing param, should return false
should.equal(Utils.isValidAddress(undefined), false);
});
it('should succeed to validate valid addresses', function () {
for (const address of testData.addresses.validAddresses) {
should.equal(Utils.isValidAddress(address), true);
}
});
});
describe('isValidBlockId', function () {
it('should fail to validate invalid Block hashes', function () {
for (const blockHash of testData.blockHashes.invalidBlockHashes) {
should.doesNotThrow(() => Utils.isValidBlockId(blockHash));
should.equal(Utils.isValidBlockId(blockHash), false);
}
});
it('should succeed to validate valid Block hashes', function () {
for (const blockHash of testData.blockHashes.validBlockHashes) {
should.equal(Utils.isValidBlockId(blockHash), true);
}
});
});
describe('isValidPublicKey', function () {
it('should fail to validate invalid public keys', function () {
for (const pubKey of testData.pubKeys.invalidPubKeys) {
should.doesNotThrow(() => Utils.isValidPublicKey(pubKey));
should.equal(Utils.isValidPublicKey(pubKey), false);
}
// @ts-expect-error Testing for missing param, should not throw an error
should.doesNotThrow(() => Utils.isValidPublicKey(undefined));
// @ts-expect-error Testing for missing param, should return false
should.equal(Utils.isValidPublicKey(undefined), false);
});
it('should succeed to validate public keys', function () {
for (const pubKey of testData.pubKeys.validPubKeys) {
should.equal(Utils.isValidPublicKey(pubKey), true);
}
});
});
describe('isValidPrivateKey', function () {
it('should fail to validate invalid private keys', function () {
for (const prvKey of testData.prvKeys.invalidPrvKeys) {
should.doesNotThrow(() => Utils.isValidPrivateKey(prvKey));
should.equal(Utils.isValidPrivateKey(prvKey), false);
}
});
it('should succeed to validate private keys', function () {
const validPrvKey = [testData.prvKeys.prvKey1.base58, testData.prvKeys.prvKey1.uint8Array];
for (const prvKey of validPrvKey) {
should.equal(Utils.isValidPrivateKey(prvKey), true);
}
});
});
describe('isValidRawTransaction', function () {
it('should fail to validate an invalid raw transaction', function () {
should.doesNotThrow(() => Utils.isValidRawTransaction(testData.INVALID_RAW_TX));
should.equal(Utils.isValidRawTransaction(testData.INVALID_RAW_TX), false);
});
it('should succeed to validate a valid raw transaction', function () {
const validRawTxs = [testData.RAW_TX_UNSIGNED, testData.RAW_TX_SIGNED];
for (const rawTx of validRawTxs) {
should.equal(Utils.isValidRawTransaction(rawTx), true);
}
});
});
describe('isValidSignature and isValidTransactionId', function () {
it('should fail to validate invalid signatures', function () {
for (const signature of testData.signatures.invalidSignatures) {
should.doesNotThrow(() => Utils.isValidSignature(signature));
should.equal(Utils.isValidSignature(signature), false);
should.doesNotThrow(() => Utils.isValidTransactionId(signature));
should.equal(Utils.isValidTransactionId(signature), false);
}
});
it('should succeed to validate valid signatures', function () {
for (const signature of testData.signatures.validSignatures) {
should.equal(Utils.isValidSignature(signature), true);
should.equal(Utils.isValidTransactionId(signature), true);
}
});
});
describe('base58 and Uint8Array encoding', function () {
it('should succeed to base58ToUint8Array', function () {
should.deepEqual(Utils.base58ToUint8Array(testData.prvKeys.prvKey1.base58), testData.prvKeys.prvKey1.uint8Array);
});
it('should succeed to Uint8ArrayTobase58', function () {
should.deepEqual(Utils.Uint8ArrayTobase58(testData.prvKeys.prvKey1.uint8Array), testData.prvKeys.prvKey1.base58);
});
});
describe('isValidAmount', function () {
it('should succeed for valid amounts', function () {
const validAmounts = ['0', '12312312'];
for (const amount of validAmounts) {
should.equal(Utils.isValidAmount(amount), true);
}
});
it('should fail for invalid amounts', function () {
const invalidAmounts = ['-1', 'randomstring', '33.04235'];
for (const amount of invalidAmounts) {
should.equal(Utils.isValidAmount(amount), false);
}
});
});
describe('verifySignature', function () {
it('should succeed for valid signature in a unsigned tx', function () {
const signature = '335sxAuVj5ucXqVWW82QwpFLArPbdD3gXfXr4KrxkLkUpmLB3Nwz2G82z2TqiDD7mNAAbHkcAqD5ycDZp1vVKtjf';
Utils.verifySignature(
testData.TOKEN_TRANSFER_UNSIGNED_TX_WITH_MEMO_AND_DURABLE_NONCE_OLD,
signature,
testData.associatedTokenAccounts.accounts[0].pub
).should.equal(true);
});
it('should succeed for valid signature in a signed tx', function () {
const signature = '335sxAuVj5ucXqVWW82QwpFLArPbdD3gXfXr4KrxkLkUpmLB3Nwz2G82z2TqiDD7mNAAbHkcAqD5ycDZp1vVKtjf';
Utils.verifySignature(
testData.TOKEN_TRANSFER_SIGNED_TX_WITH_MEMO_AND_DURABLE_NONCE_OLD,
signature,
testData.associatedTokenAccounts.accounts[0].pub
).should.equal(true);
});
it('should fail for invalid signature', function () {
const signature = '2QdKALq4adaTahJH13AGzM5bAFuNshw43iQBdVS9D2Loq736zUgPXfHj32cNJKX6FyjUzYJhGfEyAAB5FgYUW6zR';
Utils.verifySignature(
testData.TRANSFER_UNSIGNED_TX_WITH_MEMO_AND_DURABLE_NONCE,
signature,
testData.authAccount.pub
).should.equal(false);
});
it('should fail for invalid pub key', function () {
const signature = '3pD6ayWtvFkn8Fe5efYbSaCYMpnDwzDTmmeoMhcSMAcMrGvmwPFhLxok5vxhHnooA3YSXfnyZgi4e3K3sCHmgU3k';
Utils.verifySignature(
testData.TRANSFER_UNSIGNED_TX_WITH_MEMO_AND_DURABLE_NONCE,
signature,
testData.nonceAccount.pub
).should.equal(false);
});
it('should throw for invalid tx', function () {
const signature = '3pD6ayWtvFkn8Fe5efYbSaCYMpnDwzDTmmeoMhcSMAcMrGvmwPFhLxok5vxhHnooA3YSXfnyZgi4e3K3sCHmgU3k';
should(() => Utils.verifySignature(testData.INVALID_RAW_TX, signature, testData.nonceAccount.pub)).throwError(
'Invalid serializedTx'
);
});
it('should throw for invalid pubkey', function () {
const signature = '3pD6ayWtvFkn8Fe5efYbSaCYMpnDwzDTmmeoMhcSMAcMrGvmwPFhLxok5vxhHnooA3YSXfnyZgi4e3K3sCHmgU3k';
should(() =>
Utils.verifySignature(
testData.TRANSFER_UNSIGNED_TX_WITH_MEMO_AND_DURABLE_NONCE,
signature,
testData.pubKeys.invalidPubKeys[0]
)
).throwError('Invalid publicKey');
});
it('should throw for invalid signature', function () {
const signature = 'randomstring';
should(() =>
Utils.verifySignature(
testData.TRANSFER_UNSIGNED_TX_WITH_MEMO_AND_DURABLE_NONCE,
signature,
testData.nonceAccount.pub
)
).throwError('Invalid signature');
});
});
describe('isValidMemo', function () {
it('should return true for valid memo', function () {
Utils.isValidMemo('testmemo').should.equal(true);
});
it('should return false for a long memo', function () {
Utils.isValidMemo(
'3pD6ayWtvFkn8Fe5efYbSaCYMpnDwzDTmmeoMhcSMAcMrGvmwPFhLxok5vxhHnooA3YSXfnyZgi4e3K3sCHmgU3kPFhLxok5vxhHnooA3YSXfnyZgi4e3K3sCHmgU3k'
).should.equal(true);
});
});
describe('getInstructionType', function () {
it('should succeed for memo program', function () {
const memoInstruction = new TransactionInstruction({
keys: [],
programId: new PublicKey(MEMO_PROGRAM_PK),
data: Buffer.from('random memo'),
});
Utils.getInstructionType(memoInstruction).should.equal('Memo');
});
it('should succeed for system program', function () {
const fromAddress = testData.authAccount.pub;
const toAddress = testData.nonceAccount.pub;
const amount = '100000';
const transferInstruction = SystemProgram.transfer({
fromPubkey: new PublicKey(fromAddress),
toPubkey: new PublicKey(toAddress),
lamports: new BigNumber(amount).toNumber(),
});
Utils.getInstructionType(transferInstruction).should.equal('Transfer');
});
it('should fail for invalid type ', function () {
const voteAddress = 'Vote111111111111111111111111111111111111111';
const invalidInstruction = new TransactionInstruction({
keys: [],
programId: new PublicKey(voteAddress),
data: Buffer.from('random memo'),
});
should(() => Utils.getInstructionType(invalidInstruction)).throwError(
'Invalid transaction, instruction program id not supported: ' + voteAddress
);
});
});
describe('validateIntructionTypes', function () {
it('should succeed for valid instruction type', function () {
const fromAddress = testData.authAccount.pub;
const toAddress = testData.nonceAccount.pub;
const amount = '100000';
const transferInstruction = SystemProgram.transfer({
fromPubkey: new PublicKey(fromAddress),
toPubkey: new PublicKey(toAddress),
lamports: new BigNumber(amount).toNumber(),
});
should.doesNotThrow(() => Utils.validateIntructionTypes([transferInstruction]));
});
it('should fail for invalid instruction type', function () {
const accountPubkey = testData.authAccount.pub;
const assignInstruction = SystemProgram.nonceWithdraw({
noncePubkey: new PublicKey(accountPubkey),
authorizedPubkey: new PublicKey(accountPubkey),
toPubkey: new PublicKey(accountPubkey),
lamports: 200,
});
should(() => Utils.validateIntructionTypes([assignInstruction])).throwError(
'Invalid transaction, instruction type not supported: ' + Utils.getInstructionType(assignInstruction)
);
});
});
describe('validateRawTransaction', function () {
it('should succeed for valid raw transaction', function () {
should.doesNotThrow(() => Utils.validateRawTransaction(testData.RAW_TX_UNSIGNED));
});
it('should fail for invalid raw transaction', function () {
should(() => Utils.validateRawTransaction('AAAAAAAAAAAAAAAAA')).throwError('Invalid raw transaction');
});
it('should fail for missing param', function () {
// @ts-expect-error Testing for missing param, should throw error
should(() => Utils.validateRawTransaction()).throwError('Invalid raw transaction: Undefined');
});
});
describe('getSolTokenFromTokenName', function () {
it('should succeed for sol token', function () {
should.notEqual(Utils.getSolTokenFromTokenName('tsol:usdc'), undefined);
});
it('should fail for non tokens', function () {
should.equal(Utils.getSolTokenFromTokenName('tsol'), undefined);
});
it('should fail if tokenName is not in coins', function () {
should.equal(Utils.getSolTokenFromTokenName('something random'), undefined);
});
});
describe('getAssociatedTokenAccountAddress', function () {
const usdcMintAddress = testData.tokenTransfers.mintUSDC;
const tokenAddress = '141BFNem3pknc8CzPVLv1Ri3btgKdCsafYP5nXwmXfxU';
it('should succeed for native address as owner address', async function () {
const ownerAddress = testData.authAccount.pub;
const result = await Utils.getAssociatedTokenAccountAddress(usdcMintAddress, ownerAddress);
result.should.be.equal(tokenAddress);
});
it('should fail for token address as owner address', async function () {
const invalidOwnerAddress = tokenAddress;
await Utils.getAssociatedTokenAccountAddress(usdcMintAddress, invalidOwnerAddress).should.be.rejectedWith(
'Invalid ownerAddress - address off ed25519 curve, got: ' + invalidOwnerAddress
);
});
});
describe('matchTransactionTypeByInstructionsOrder', function () {
describe('Activate stake instructions', function () {
it('should match staking activate instructions', function () {
const fromAccount = new PublicKey(testData.authAccount.pub);
const stakingAccount = new PublicKey(testData.stakeAccount.pub);
const validator = new PublicKey(testData.validator.pub);
const amount = '100000';
// Instructions
const stakingActivateInstructions = StakeProgram.createAccount({
fromPubkey: fromAccount,
stakePubkey: stakingAccount,
authorized: {
staker: fromAccount,
withdrawer: fromAccount,
},
lockup: new Lockup(0, 0, fromAccount),
lamports: new BigNumber(amount).toNumber(),
}).instructions;
const stakingDelegateInstructions = StakeProgram.delegate({
authorizedPubkey: fromAccount,
stakePubkey: stakingAccount,
votePubkey: validator,
}).instructions;
const instructions = [...stakingActivateInstructions, ...stakingDelegateInstructions];
const isAMatch = Utils.matchTransactionTypeByInstructionsOrder(
instructions,
stakingActivateInstructionsIndexes
);
isAMatch.should.be.true();
});
it('should match staking activate instructions with memo and durable nonce', function () {
const fromAccount = new PublicKey(testData.authAccount.pub);
const nonceAccount = testData.nonceAccount.pub;
const stakingAccount = new PublicKey(testData.stakeAccount.pub);
const validator = new PublicKey(testData.validator.pub);
const amount = '100000';
const memo = 'test memo';
// Instructions
const nonceAdvanceInstruction = SystemProgram.nonceAdvance({
noncePubkey: new PublicKey(nonceAccount),
authorizedPubkey: fromAccount,
});
const stakingActivateInstructions = StakeProgram.createAccount({
fromPubkey: fromAccount,
stakePubkey: stakingAccount,
authorized: {
staker: fromAccount,
withdrawer: fromAccount,
},
lockup: new Lockup(0, 0, fromAccount),
lamports: new BigNumber(amount).toNumber(),
}).instructions;
const stakingDelegateInstructions = StakeProgram.delegate({
authorizedPubkey: fromAccount,
stakePubkey: stakingAccount,
votePubkey: validator,
}).instructions;
const memoInstruction = new TransactionInstruction({
keys: [],
programId: new PublicKey(MEMO_PROGRAM_PK),
data: Buffer.from(memo),
});
const instructions = [
nonceAdvanceInstruction,
...stakingActivateInstructions,
...stakingDelegateInstructions,
memoInstruction,
];
const isAMatch = Utils.matchTransactionTypeByInstructionsOrder(
instructions,
stakingActivateInstructionsIndexes
);
isAMatch.should.be.true();
});
});
describe('Deactivate stake instructions', function () {
it('should match staking deactivate instructions', function () {
const fromAccount = new PublicKey(testData.authAccount.pub);
const stakingAccount = new PublicKey(testData.stakeAccount.pub);
// Instructions
const stakingDeactivateInstructions = StakeProgram.deactivate({
authorizedPubkey: fromAccount,
stakePubkey: stakingAccount,
}).instructions;
const instructions = [...stakingDeactivateInstructions];
const isAMatch = Utils.matchTransactionTypeByInstructionsOrder(
instructions,
stakingDeactivateInstructionsIndexes
);
isAMatch.should.be.true();
});
it('should match staking deactivate instructions with memo and durable nonce', function () {
const fromAccount = new PublicKey(testData.authAccount.pub);
const nonceAccount = testData.nonceAccount.pub;
const stakingAccount = new PublicKey(testData.stakeAccount.pub);
const memo = 'test memo';
// Instructions
const nonceAdvanceInstruction = SystemProgram.nonceAdvance({
noncePubkey: new PublicKey(nonceAccount),
authorizedPubkey: fromAccount,
});
const stakingDeactivateInstructions = StakeProgram.deactivate({
authorizedPubkey: fromAccount,
stakePubkey: stakingAccount,
}).instructions;
const memoInstruction = new TransactionInstruction({
keys: [],
programId: new PublicKey(MEMO_PROGRAM_PK),
data: Buffer.from(memo),
});
const instructions = [nonceAdvanceInstruction, ...stakingDeactivateInstructions, memoInstruction];
const isAMatch = Utils.matchTransactionTypeByInstructionsOrder(
instructions,
stakingDeactivateInstructionsIndexes
);
isAMatch.should.be.true();
});
});
describe('Staking withdraw instructions', function () {
it('should match staking withdraw instructions', function () {
const fromAccount = new PublicKey(testData.authAccount.pub);
const stakingAccount = new PublicKey(testData.stakeAccount.pub);
const amount = '100000';
// Instructions
const stakingWithdrawInstructions = StakeProgram.withdraw({
authorizedPubkey: fromAccount,
stakePubkey: stakingAccount,
toPubkey: fromAccount,
lamports: new BigNumber(amount).toNumber(),
}).instructions;
const instructions = [...stakingWithdrawInstructions];
const isAMatch = Utils.matchTransactionTypeByInstructionsOrder(
instructions,
stakingWithdrawInstructionsIndexes
);
isAMatch.should.be.true();
});
it('should match staking withdraw instructions with memo and durable nonce', function () {
const fromAccount = new PublicKey(testData.authAccount.pub);
const nonceAccount = testData.nonceAccount.pub;
const stakingAccount = new PublicKey(testData.stakeAccount.pub);
const amount = '100000';
const memo = 'test memo';
// Instructions
const nonceAdvanceInstruction = SystemProgram.nonceAdvance({
noncePubkey: new PublicKey(nonceAccount),
authorizedPubkey: fromAccount,
});
// Instructions
const stakingWithdrawInstructions = StakeProgram.withdraw({
authorizedPubkey: fromAccount,
stakePubkey: stakingAccount,
toPubkey: fromAccount,
lamports: new BigNumber(amount).toNumber(),
}).instructions;
const memoInstruction = new TransactionInstruction({
keys: [],
programId: new PublicKey(MEMO_PROGRAM_PK),
data: Buffer.from(memo),
});
const instructions = [nonceAdvanceInstruction, ...stakingWithdrawInstructions, memoInstruction];
const isAMatch = Utils.matchTransactionTypeByInstructionsOrder(
instructions,
stakingWithdrawInstructionsIndexes
);
isAMatch.should.be.true();
});
});
});
});
Выполнить команду
Для локальной разработки. Не используйте в интернете!