|
| 1 | +// Import the 'should' assertion library for testing |
| 2 | +import 'should'; |
| 3 | +// Import BitGo testing utilities |
| 4 | +import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test'; |
| 5 | +import { BitGoAPI } from '@bitgo/sdk-api'; |
| 6 | +// Import core types for transaction verification |
| 7 | +import { VerifyTransactionOptions } from '@bitgo/sdk-core'; |
| 8 | +// Import NEAR-specific types and classes |
| 9 | +import { TransactionPrebuild, Near } from '../../src/near'; |
| 10 | +import { TNear as TNearCoin } from '../../src/tnear'; |
| 11 | +// Import test data containing sample transactions and account info |
| 12 | +import * as testData from '../resources/near'; |
| 13 | + |
| 14 | +/** |
| 15 | + * Test suite for NEAR token enablement validation |
| 16 | + * |
| 17 | + * Token enablement is a process where users must "enable" a token before they can receive it. |
| 18 | + * This involves creating a storage deposit transaction that allocates space on the blockchain |
| 19 | + * for the token balance. This test validates that the security checks work correctly. |
| 20 | + */ |
| 21 | +describe('NEAR Token Enablement Validation', function () { |
| 22 | + let bitgo: TestBitGoAPI; // BitGo API instance for testing |
| 23 | + let basecoin: Near; // NEAR coin implementation instance |
| 24 | + |
| 25 | + // Setup that runs once before all tests |
| 26 | + before(function () { |
| 27 | + // Create a test BitGo instance configured for the test environment |
| 28 | + bitgo = TestBitGo.decorate(BitGoAPI, { env: 'test' }); |
| 29 | + // Register the TNEAR (testnet NEAR) coin with BitGo |
| 30 | + bitgo.safeRegister('tnear', TNearCoin.createInstance); |
| 31 | + // Get the NEAR coin instance we'll use for testing |
| 32 | + basecoin = bitgo.coin('tnear') as Near; |
| 33 | + }); |
| 34 | + |
| 35 | + /** |
| 36 | + * Helper function to create valid transaction parameters for token enablement |
| 37 | + * @returns Transaction parameters that should pass validation |
| 38 | + */ |
| 39 | + const createValidTxParams = () => ({ |
| 40 | + type: 'enabletoken' as const, // Tells BitGo this is a token enablement transaction |
| 41 | + recipients: [ |
| 42 | + { |
| 43 | + address: testData.accounts.account1.address, // The account that will receive the token capability |
| 44 | + amount: '0', // Token enablement typically has 0 amount (no tokens transferred, just enabling) |
| 45 | + tokenName: 'tnear:tnep24dp', // The specific token being enabled (testnet NEP-141 token) |
| 46 | + }, |
| 47 | + ], |
| 48 | + }); |
| 49 | + |
| 50 | + /** |
| 51 | + * Helper function to create a transaction prebuild object |
| 52 | + * @param txHex - The raw transaction hex string |
| 53 | + * @returns A TransactionPrebuild object with the hex and metadata |
| 54 | + */ |
| 55 | + const createTxPrebuild = (txHex: string): TransactionPrebuild => ({ |
| 56 | + txHex, // The actual transaction data in hexadecimal format |
| 57 | + key: 'test-key', // Public key (placeholder for testing) |
| 58 | + blockHash: 'test-block-hash', // Recent block hash (placeholder for testing) |
| 59 | + nonce: BigInt(1), // Transaction nonce to prevent replay attacks |
| 60 | + }); |
| 61 | + |
| 62 | + /** |
| 63 | + * TEST 1: Happy Path - Valid Token Enablement Transaction |
| 64 | + * |
| 65 | + * This test verifies that a properly formed token enablement transaction passes validation. |
| 66 | + * It uses a real storage deposit transaction hex from the test data. |
| 67 | + */ |
| 68 | + it('should validate valid token enablement transaction', async function () { |
| 69 | + // Create valid transaction parameters |
| 70 | + const txParams = createValidTxParams(); |
| 71 | + |
| 72 | + // Create transaction prebuild using a real storage deposit transaction |
| 73 | + // This hex represents a NEAR transaction that creates storage space for a token |
| 74 | + const txPrebuild = createTxPrebuild(testData.rawTx.selfStorageDeposit.unsigned); |
| 75 | + |
| 76 | + // Prepare the verification options that would be passed to BitGo |
| 77 | + const verifyOptions: VerifyTransactionOptions = { |
| 78 | + txParams, // What the user thinks they're signing |
| 79 | + txPrebuild, // The actual transaction hex from BitGo |
| 80 | + wallet: { id: 'test-wallet' } as any, // Mock wallet object |
| 81 | + }; |
| 82 | + |
| 83 | + // This should NOT throw an error - the transaction should be valid |
| 84 | + // The verifyTransaction method will call validateTokenEnablementTransaction internally |
| 85 | + await basecoin.verifyTransaction(verifyOptions); |
| 86 | + }); |
| 87 | + |
| 88 | + /** |
| 89 | + * TEST 2: Security Test - Transaction Hex Mismatch Detection |
| 90 | + * |
| 91 | + * This test verifies that the validation catches when the transaction hex doesn't match |
| 92 | + * what the user expects to sign. This is a critical security check to prevent attacks |
| 93 | + * where a malicious actor substitutes a different transaction. |
| 94 | + */ |
| 95 | + it('should reject transaction with mismatched hex', async function () { |
| 96 | + // Create token enablement parameters but with the recipient address that matches the transfer transaction |
| 97 | + const txParams = { |
| 98 | + type: 'enabletoken' as const, |
| 99 | + recipients: [ |
| 100 | + { |
| 101 | + address: testData.accounts.account2.address, // This matches the transfer transaction recipient |
| 102 | + amount: '0', |
| 103 | + tokenName: 'tnear:tnep24dp', |
| 104 | + }, |
| 105 | + ], |
| 106 | + }; |
| 107 | + |
| 108 | + // BUT use a DIFFERENT transaction type (regular transfer instead of storage deposit) |
| 109 | + // This simulates an attack where someone tries to trick the user into signing |
| 110 | + // a different transaction than what they think they're signing |
| 111 | + const txPrebuild = createTxPrebuild(testData.rawTx.transfer.unsigned); // Different transaction type |
| 112 | + |
| 113 | + const verifyOptions: VerifyTransactionOptions = { |
| 114 | + txParams, // User thinks they're enabling a token |
| 115 | + txPrebuild, // But the actual hex is for a money transfer! |
| 116 | + wallet: { id: 'test-wallet' } as any, |
| 117 | + }; |
| 118 | + |
| 119 | + // This SHOULD throw an error because the hex doesn't match the expected transaction type |
| 120 | + // The validation will detect that the transaction outputs don't match the expected token enablement parameters |
| 121 | + await basecoin |
| 122 | + .verifyTransaction(verifyOptions) |
| 123 | + .should.be.rejectedWith('Tx outputs does not match with expected txParams recipients'); |
| 124 | + }); |
| 125 | + |
| 126 | + /** |
| 127 | + * TEST 3: Security Test - Address Mismatch Detection |
| 128 | + * |
| 129 | + * This test verifies that the validation catches when the recipient address in the |
| 130 | + * transaction parameters doesn't match the actual address in the transaction hex. |
| 131 | + * This prevents attacks where someone changes the destination address. |
| 132 | + */ |
| 133 | + it('should reject transaction with address mismatch', async function () { |
| 134 | + // Create transaction parameters with a WRONG address |
| 135 | + const txParams = { |
| 136 | + type: 'enabletoken' as const, |
| 137 | + recipients: [ |
| 138 | + { |
| 139 | + address: 'wrong.address.near', // This doesn't match the address in the transaction hex |
| 140 | + amount: '0', |
| 141 | + tokenName: 'tnear:tnep24dp', |
| 142 | + }, |
| 143 | + ], |
| 144 | + }; |
| 145 | + |
| 146 | + // Use the correct storage deposit transaction hex |
| 147 | + const txPrebuild = createTxPrebuild(testData.rawTx.selfStorageDeposit.unsigned); |
| 148 | + |
| 149 | + const verifyOptions: VerifyTransactionOptions = { |
| 150 | + txParams, // Contains wrong address |
| 151 | + txPrebuild, // Contains correct address in the hex |
| 152 | + wallet: { id: 'test-wallet' } as any, |
| 153 | + }; |
| 154 | + |
| 155 | + // This SHOULD throw an error because the addresses don't match |
| 156 | + // The validateTokenEnablementTransaction method should detect this mismatch |
| 157 | + // and prevent the user from being tricked into enabling tokens for the wrong address |
| 158 | + await basecoin.verifyTransaction(verifyOptions).should.be.rejectedWith('Address mismatch: wrong.address.near'); |
| 159 | + }); |
| 160 | +}); |
0 commit comments