Skip to content

Commit

Permalink
feat: cli option to decode serialized Clarity values (#1599)
Browse files Browse the repository at this point in the history
* feat: cli option to decode serialized Clarity values

* chore: typo in help desc

* chore: change group
  • Loading branch information
zone117x authored Nov 28, 2023
1 parent da86f99 commit 25c821b
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 0 deletions.
31 changes: 31 additions & 0 deletions packages/cli/src/argparse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,37 @@ export const CLI_ARGS = {
'\n',
group: 'Account Management',
},
decode_cv: {
type: 'array',
items: [
{
name: 'clarity_value',
type: 'string',
realtype: 'string',
pattern: '-|^(0x|0X)?[a-fA-F0-9]+$',
},
{
name: 'format',
type: 'string',
realtype: 'format',
pattern: '^(repr|pretty|json)$',
},
],
minItems: 1,
maxItems: 4,
help:
'Decode a serialized Clarity value.\n' +
'\n' +
'Example:\n' +
'\n' +
' $ stx decode_cv 0x050011deadbeef11ababffff11deadbeef11ababffff\n' +
' S08XXBDYXW8TQAZZZW8XXBDYXW8TQAZZZZ88551S\n' +
' $ stx decode_cv --format json SPA2MZWV9N67TBYVWTE0PSSKMJ2F6YXW7CBE6YPW\n' +
' {"type":"principal","value":"S08XXBDYXW8TQAZZZW8XXBDYXW8TQAZZZZ88551S"}\n' +
' $ echo 0x050011deadbeef11ababffff11deadbeef11ababffff | stx decode_cv -\n' +
' S08XXBDYXW8TQAZZZW8XXBDYXW8TQAZZZZ88551S\n',
group: 'Utilities',
},
convert_address: {
type: 'array',
items: [
Expand Down
35 changes: 35 additions & 0 deletions packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import {
TransactionVersion,
TxBroadcastResult,
validateContractCall,
Cl,
cvToJSON,
} from '@stacks/transactions';
import express from 'express';
import { prompt } from 'inquirer';
Expand Down Expand Up @@ -952,6 +954,37 @@ async function readOnlyContractFunctionCall(
});
}

/*
* Decode a serialized Clarity value
* args:
* @value (string) the hex string of the serialized value, or '-' to read from stdin
* @format (string) the format to output the value in; one of 'pretty', 'json', or 'repr'
*/
function decodeCV(_network: CLINetworkAdapter, args: string[]): Promise<string> {
const inputArg = args[0];
const format = args[1];

let inputValue: string;
if (inputArg === '-') {
inputValue = fs.readFileSync(process.stdin.fd, 'utf-8').trim();
} else {
inputValue = inputArg;
}

const cv = Cl.deserialize(inputValue);
let cvString: string;
if (format === 'pretty') {
cvString = Cl.prettyPrint(cv, 2);
} else if (format === 'json') {
cvString = JSON.stringify(cvToJSON(cv));
} else if (format === 'repr' || !format) {
cvString = cvToString(cv);
} else {
throw new Error('Invalid format option');
}
return Promise.resolve(cvString);
}

// /*
// * Get the number of confirmations of a txid.
// * args:
Expand Down Expand Up @@ -1959,6 +1992,7 @@ const COMMANDS: Record<string, CommandFunction> = {
can_stack: canStack,
call_contract_func: contractFunctionCall,
call_read_only_contract_func: readOnlyContractFunctionCall,
decode_cv: decodeCV,
convert_address: addressConvert,
decrypt_keychain: decryptMnemonic,
deploy_contract: contractDeploy,
Expand Down Expand Up @@ -2159,6 +2193,7 @@ export const testables =
process.env.NODE_ENV === 'test'
? {
addressConvert,
decodeCV,
canStack,
contractFunctionCall,
getStacksWalletKey,
Expand Down
36 changes: 36 additions & 0 deletions packages/cli/tests/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { CLIMain, testables } from '../src/cli';
import { CLINetworkAdapter, CLI_NETWORK_OPTS, getNetwork } from '../src/network';

import {
Cl,
ClarityAbi,
createStacksPrivateKey,
publicKeyFromSignatureVrs,
Expand All @@ -23,6 +24,7 @@ import {
WalletKeyInfoResult,
} from './derivation-path/keychain';
import * as fixtures from './fixtures/cli.fixture';
import { bytesToHex } from '@stacks/common';

const TEST_ABI: ClarityAbi = JSON.parse(
readFileSync(path.join(__dirname, './abi/test-abi.json')).toString()
Expand All @@ -34,6 +36,7 @@ jest.mock('inquirer');

const {
addressConvert,
decodeCV,
canStack,
contractFunctionCall,
getStacksWalletKey,
Expand All @@ -53,6 +56,39 @@ const testnetNetwork = new CLINetworkAdapter(
{} as CLI_NETWORK_OPTS
);

describe('decode_cv', () => {
test('Should decode from hex arg', async () => {
const result = await decodeCV(mainnetNetwork, [
'0x050011deadbeef11ababffff11deadbeef11ababffff',
]);
expect(result).toEqual('S08XXBDYXW8TQAZZZW8XXBDYXW8TQAZZZZ88551S');
});

test('Should decode from hex to json', async () => {
const result = await decodeCV(mainnetNetwork, [
'0x050011deadbeef11ababffff11deadbeef11ababffff',
'json',
]);
expect(result).toEqual(
'{"type":"principal","value":"S08XXBDYXW8TQAZZZW8XXBDYXW8TQAZZZZ88551S"}'
);
});

test('Should decode from hex to repr', async () => {
const list = Cl.list([1, 2, 3].map(Cl.int));
const serialized = bytesToHex(Cl.serialize(list));
const result = await decodeCV(mainnetNetwork, [serialized, 'repr']);
expect(result).toEqual('(list 1 2 3)');
});

test('Should decode from hex to pretty print', async () => {
const list = Cl.list([1, 2, 3].map(Cl.int));
const serialized = bytesToHex(Cl.serialize(list));
const result = await decodeCV(mainnetNetwork, [serialized, 'pretty']);
expect(result).toEqual('(list\n 1\n 2\n 3\n)');
});
});

describe('convert_address', () => {
test.each(fixtures.convertAddress)('%p - testnet: %p', async (input, testnet, expectedResult) => {
const network = testnet ? testnetNetwork : mainnetNetwork;
Expand Down

1 comment on commit 25c821b

@vercel
Copy link

@vercel vercel bot commented on 25c821b Nov 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.