Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion modules/sdk-coin-hash/src/lib/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const validDenoms = ['nhash', 'uhash', 'mhash', 'hash'];
export const validDenoms = ['nhash', 'uhash', 'mhash', 'hash', 'uylds.fcc'];
export const mainnetAccountAddressRegex = /^(pb)1(['qpzry9x8gf2tvdw0s3jn54khce6mua7l]{38})$/;
export const mainnetValidatorAddressRegex = /^(pbvaloper)1(['qpzry9x8gf2tvdw0s3jn54khce6mua7l]{38})$/;
export const mainnetContractAddressRegex = /^(pb)1(['qpzry9x8gf2tvdw0s3jn54khce6mua7l]+)$/;
Expand Down
72 changes: 72 additions & 0 deletions modules/sdk-coin-hash/test/resources/hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,39 @@ export const TEST_SEND_TX = {
},
};

export const TEST_SEND_TOKEN_TX = {
hash: 'A5EFED6B56EACCF531C9A7FC13731A9E0AF6F7101876995035D9AD6DA40C3B2D',
signature: 'JI7RpMouIn5VAoYpLH171tGogINLbQjDsyJ33lsWDPkYVF3kc39YDBo0sRq9fXEeZedP/pEh1f7fXPxaZisIFw==',
pubKey: 'Asujzd7qXNDrdmH8ZbeVDtZbunQiYhlZNt5Qw7J3E0Me',
privateKey: 'qp/Z1b0NeHgROzRAOqSxpBLZydY89cHSgWquf5/0nOs=',
signedTxBase64:
'CokBCoYBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmYKKXRwMWxmbXp4bG5wZjhrNXJxOThqeGV0ajUyZW5mMGFoenF6MDY5c3kzEil0cDEzZmE3amE4ZnhyejV3aDJka3dmZjlwbGNxZ2NqYTNycHkzeXFwORoOCgl1eWxkcy5mY2MSATESbQpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAsujzd7qXNDrdmH8ZbeVDtZbunQiYhlZNt5Qw7J3E0MeEgQKAggBGBISGQoTCgVuaGFzaBIKMzgxMDAwMDAwMBDAmgwaQCSO0aTKLiJ+VQKGKSx9e9bRqICDS20Iw7Mid95bFgz5GFRd5HN/WAwaNLEavX1xHmXnT/6RIdX+31z8WmYrCBc=',
sender: 'tp1lfmzxlnpf8k5rq98jxetj52enf0ahzqz069sy3',
recipient: 'tp13fa7ja8fxrz5wh2dkwff9plcqgcja3rpy3yqp9',
chainId: 'pio-testnet-1',
accountNumber: 235524,
sequence: 18,
sendAmount: '1',
feeAmount: '3810000000',
sendMessage: {
typeUrl: '/cosmos.bank.v1beta1.MsgSend',
value: {
amount: [
{
denom: 'uylds.fcc',
amount: '1',
},
],
toAddress: 'tp13fa7ja8fxrz5wh2dkwff9plcqgcja3rpy3yqp9',
fromAddress: 'tp1lfmzxlnpf8k5rq98jxetj52enf0ahzqz069sy3',
},
},
gasBudget: {
amount: [{ denom: 'nhash', amount: '3810000000' }],
gasLimit: 200000,
},
};

export const TEST_DELEGATE_TX = {
hash: 'BD887ADE5E10C378B4B3DB3E4E0D5D77F0C2F3DFEE1A352BF186B22A8F022967',
signature: 'FMZqHoNhxzJRvhhZbiLLybgAHdkszEstxow2oj1T1It8OyEGLJ8o2jnUo9OuCfeCKBfcWrKAi4w9dwsC8rBChQ==',
Expand Down Expand Up @@ -194,6 +227,45 @@ export const TEST_TX_WITH_MEMO = {
},
};

export const TEST_TOKEN_TX_WITH_MEMO = {
hash: 'F4B979ECBF56EA203EA24ECE1F44AD621C5BC97955722C16AFBFC902E0001E5D',
signature: 'HkE/roZVKf3rj4RH07fPPLeSJzxSGmz+7FQJ6nom1e9pD/3QMLXVHLpswFJCNDwoS9kfeSifBFKgPc6dpOr+7w==',
pubKey: 'Asujzd7qXNDrdmH8ZbeVDtZbunQiYhlZNt5Qw7J3E0Me',
privateKey: 'qp/Z1b0NeHgROzRAOqSxpBLZydY89cHSgWquf5/0nOs=',
signedTxBase64:
'CowBCoYBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmYKKXRwMWxmbXp4bG5wZjhrNXJxOThqeGV0ajUyZW5mMGFoenF6MDY5c3kzEil0cDEzZmE3amE4ZnhyejV3aDJka3dmZjlwbGNxZ2NqYTNycHkzeXFwORoOCgl1eWxkcy5mY2MSATESATcSbQpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAsujzd7qXNDrdmH8ZbeVDtZbunQiYhlZNt5Qw7J3E0MeEgQKAggBGBESGQoTCgVuaGFzaBIKMzgxMDAwMDAwMBDAmgwaQB5BP66GVSn964+ER9O3zzy3kic8Uhps/uxUCep6JtXvaQ/90DC11Ry6bMBSQjQ8KEvZH3konwRSoD3OnaTq/u8=',
from: 'tp1lfmzxlnpf8k5rq98jxetj52enf0ahzqz069sy3',
to: 'tp13fa7ja8fxrz5wh2dkwff9plcqgcja3rpy3yqp9',
chainId: 'pio-testnet-1',
accountNumber: 235524,
sequence: 17,
sendAmount: '1',
feeAmount: '3810000000',
sendMessage: {
typeUrl: '/cosmos.bank.v1beta1.MsgSend',
value: {
amount: [
{
denom: 'uylds.fcc',
amount: '1',
},
],
toAddress: 'tp13fa7ja8fxrz5wh2dkwff9plcqgcja3rpy3yqp9',
fromAddress: 'tp1lfmzxlnpf8k5rq98jxetj52enf0ahzqz069sy3',
},
},
memo: '7',
gasBudget: {
amount: [
{
denom: 'nhash',
amount: '3810000000',
},
],
gasLimit: 200000,
},
};

export const mainnetAddress = {
address1: 'pb1fmxzuzx5c4ja50vu94nt0aessnuedzmppde8qr',
address2: 'pb16vmp7sz28pnvgz6f3zm6q93y39jsd33aazwg4u',
Expand Down
130 changes: 130 additions & 0 deletions modules/sdk-coin-hash/test/unit/tokenTransaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { toHex, TransactionType } from '@bitgo/sdk-core';
import { coins } from '@bitgo/statics';
import { fromBase64 } from '@cosmjs/encoding';
import should from 'should';

import { CosmosTransaction, SendMessage } from '@bitgo/abstract-cosmos';
import { HashUtils } from '../../src/lib/utils';
import * as testData from '../resources/hash';

describe('Hash Token Transaction', () => {
let tx: CosmosTransaction;
const tokenName = 'thash:ylds';
const config = coins.get(tokenName);
const utils = new HashUtils(config.network.type);

beforeEach(() => {
tx = new CosmosTransaction(config, utils);
});

describe('Empty transaction', () => {
it('should throw empty transaction', function () {
should.throws(() => tx.toBroadcastFormat(), 'Empty transaction');
});
});

describe('From raw transaction', () => {
it('should build a transfer from raw signed base64', function () {
tx.enrichTransactionDetailsFromRawTransaction(testData.TEST_SEND_TX.signedTxBase64);
const json = tx.toJson();
should.equal(json.sequence, testData.TEST_SEND_TX.sequence);
should.deepEqual(json.gasBudget, testData.TEST_SEND_TX.gasBudget);
should.equal(json.publicKey, toHex(fromBase64(testData.TEST_SEND_TX.pubKey)));
should.equal(
(json.sendMessages[0].value as SendMessage).toAddress,
testData.TEST_SEND_TX.sendMessage.value.toAddress
);
should.deepEqual(
(json.sendMessages[0].value as SendMessage).amount,
testData.TEST_SEND_TX.sendMessage.value.amount
);
should.equal(Buffer.from(json.signature as any).toString('base64'), testData.TEST_SEND_TX.signature);
should.equal(tx.type, TransactionType.Send);
tx.loadInputsAndOutputs();
should.deepEqual(tx.inputs, [
{
address: testData.TEST_SEND_TX.sender,
value: testData.TEST_SEND_TX.sendMessage.value.amount[0].amount,
coin: tokenName,
},
]);
should.deepEqual(tx.outputs, [
{
address: testData.TEST_SEND_TX.sendMessage.value.toAddress,
value: testData.TEST_SEND_TX.sendMessage.value.amount[0].amount,
coin: tokenName,
},
]);
});

it('should build a transfer from raw signed hex', function () {
tx.enrichTransactionDetailsFromRawTransaction(toHex(fromBase64(testData.TEST_SEND_TX.signedTxBase64)));
const json = tx.toJson();
should.equal(json.sequence, testData.TEST_SEND_TX.sequence);
should.deepEqual(json.gasBudget, testData.TEST_SEND_TX.gasBudget);
should.equal(json.publicKey, toHex(fromBase64(testData.TEST_SEND_TX.pubKey)));
should.equal(
(json.sendMessages[0].value as SendMessage).toAddress,
testData.TEST_SEND_TX.sendMessage.value.toAddress
);
should.deepEqual(
(json.sendMessages[0].value as SendMessage).amount,
testData.TEST_SEND_TX.sendMessage.value.amount
);
should.equal(Buffer.from(json.signature as any).toString('base64'), testData.TEST_SEND_TX.signature);
should.equal(tx.type, TransactionType.Send);
tx.loadInputsAndOutputs();
should.deepEqual(tx.inputs, [
{
address: testData.TEST_SEND_TX.sender,
value: testData.TEST_SEND_TX.sendMessage.value.amount[0].amount,
coin: tokenName,
},
]);
should.deepEqual(tx.outputs, [
{
address: testData.TEST_SEND_TX.sendMessage.value.toAddress,
value: testData.TEST_SEND_TX.sendMessage.value.amount[0].amount,
coin: tokenName,
},
]);
});

it('should fail to build a transfer from incorrect raw hex', function () {
should.throws(
() => tx.enrichTransactionDetailsFromRawTransaction('random' + testData.TEST_SEND_TX.signedTxBase64),
'incorrect raw data'
);
});

it('should fail to explain transaction with invalid raw hex', function () {
should.throws(() => tx.enrichTransactionDetailsFromRawTransaction('randomString'), 'Invalid transaction');
});
});

describe('Explain transaction', () => {
it('should explain a transfer pay transaction', function () {
tx.enrichTransactionDetailsFromRawTransaction(testData.TEST_SEND_TX.signedTxBase64);
const explainedTransaction = tx.explainTransaction();
explainedTransaction.should.deepEqual({
displayOrder: ['id', 'outputs', 'outputAmount', 'changeOutputs', 'changeAmount', 'fee', 'type'],
id: testData.TEST_SEND_TX.hash,
outputs: [
{
address: testData.TEST_SEND_TX.recipient,
amount: testData.TEST_SEND_TX.sendAmount,
},
],
outputAmount: testData.TEST_SEND_TX.sendAmount,
changeOutputs: [],
changeAmount: '0',
fee: { fee: testData.TEST_SEND_TX.feeAmount },
type: 0,
});
});

it('should fail to explain transaction with invalid raw base64 string', function () {
should.throws(() => tx.enrichTransactionDetailsFromRawTransaction('randomString'), 'Invalid transaction');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,19 @@ describe('Hash Transaction Builder', async () => {
});

const testTxData = testData.TEST_SEND_TX;
const tokenTestTxData = testData.TEST_SEND_TOKEN_TX;
let data;

beforeEach(() => {
data = [
{
type: TransactionType.Send,
testTx: testData.TEST_SEND_TX,
testTx: testTxData,
builder: factory.getTransferBuilder(),
},
{
type: TransactionType.Send,
testTx: tokenTestTxData,
builder: factory.getTransferBuilder(),
},
{
Expand Down Expand Up @@ -56,6 +62,15 @@ describe('Hash Transaction Builder', async () => {
should.equal(rawTx, testTxData.signedTxBase64);
});

it('should build a signed token tx from signed token tx data', async function () {
const txBuilder = factory.from(tokenTestTxData.signedTxBase64);
const tx = await txBuilder.build();
should.equal(tx.type, TransactionType.Send);
// Should recreate the same raw tx data when re-build and turned to broadcast format
const rawTx = tx.toBroadcastFormat();
should.equal(rawTx, tokenTestTxData.signedTxBase64);
});

describe('gasBudget tests', async () => {
it('should succeed for valid gasBudget', function () {
for (const { builder } of data) {
Expand Down
Loading