(output: Output, network: Network, changeLocks: Record<string, boolean>)
| 61 | } |
| 62 | |
| 63 | export function validateOutput(output: Output, network: Network, changeLocks: Record<string, boolean>): void { |
| 64 | validateChangeLocks(changeLocks, output.token?.category); |
| 65 | |
| 66 | if (isOpReturnOutput(output)) { |
| 67 | const OP_RETURN_MAX_SIZE = 223; |
| 68 | if (output.to.byteLength > OP_RETURN_MAX_SIZE) { |
| 69 | throw new OpReturnSizeTooLargeError(output.to.byteLength, OP_RETURN_MAX_SIZE); |
| 70 | } |
| 71 | return; |
| 72 | } |
| 73 | |
| 74 | const minimumAmount = calculateDust(output); |
| 75 | if (output.amount < minimumAmount) { |
| 76 | throw new OutputSatoshisTooSmallError(output.amount, BigInt(minimumAmount)); |
| 77 | } |
| 78 | |
| 79 | if (output.token) { |
| 80 | if (output.token.amount < 0n) { |
| 81 | throw new OutputTokenAmountTooSmallError(output.token.amount); |
| 82 | } |
| 83 | |
| 84 | if (typeof output.token.category !== 'string' || !isHex(output.token.category)) { |
| 85 | throw new OutputTokenCategoryInvalidError(output.token.category); |
| 86 | } |
| 87 | |
| 88 | if (output.token.nft && (typeof output.token.nft.commitment !== 'string' || !isHex(output.token.nft.commitment))) { |
| 89 | throw new OutputTokenCommitmentInvalidError(output.token.nft.commitment); |
| 90 | } |
| 91 | |
| 92 | const NFT_COMMITMENT_MAX_SIZE = 128; |
| 93 | if (output.token.nft && output.token.nft.commitment.length > NFT_COMMITMENT_MAX_SIZE * 2) { |
| 94 | throw new OutputTokenCommitmentTooLongError(output.token.nft.commitment, NFT_COMMITMENT_MAX_SIZE); |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | // If the output is not an address (so it is P2S), then we don't need to do any address validation |
| 99 | if (typeof output.to !== 'string') return; |
| 100 | |
| 101 | if (output.token && !isTokenAddress(output.to)) { |
| 102 | throw new TokensToNonTokenAddressError(output.to); |
| 103 | } |
| 104 | |
| 105 | const addressPrefix = getNetworkPrefixForAddress(output.to); |
| 106 | const networkPrefix = getNetworkPrefix(network); |
| 107 | if (addressPrefix !== networkPrefix) { |
| 108 | throw new OutputAddressNetworkMismatchError(output.to, networkPrefix); |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | function validateChangeLocks(changeLocks: Record<string, boolean>, category?: string): void { |
| 113 | if (changeLocks.BCH) { |
no test coverage detected