Skip to content

Commit

Permalink
fix: cli add missing clarity types in contract call
Browse files Browse the repository at this point in the history
  • Loading branch information
ahsan-javaid authored and reedrosenbluth committed Sep 7, 2021
1 parent 91e65e4 commit a02665a
Show file tree
Hide file tree
Showing 4 changed files with 269 additions and 3 deletions.
1 change: 1 addition & 0 deletions packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1964,5 +1964,6 @@ export const testables =
process.env.NODE_ENV === 'test'
? {
addressConvert,
contractFunctionCall,
}
: undefined;
31 changes: 29 additions & 2 deletions packages/cli/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
getTypeString,
ClarityAbiType,
isClarityAbiPrimitive,
isClarityAbiStringAscii,
isClarityAbiStringUtf8,
isClarityAbiBuffer,
isClarityAbiResponse,
isClarityAbiOptional,
Expand All @@ -20,6 +22,9 @@ import {
intCV,
uintCV,
bufferCVFromString,
stringAsciiCV,
stringUtf8CV,
someCV,
trueCV,
falseCV,
standardPrincipalCV,
Expand Down Expand Up @@ -837,10 +842,26 @@ export function argToPrompt(arg: ClarityFunctionArg): InquirerPrompt {
name,
message: `Enter value for function argument "${name}" of type ${typeString}`,
};
} else if (isClarityAbiStringAscii(type)) {
return {
type: 'input',
name,
message: `Enter value for function argument "${name}" of type ${typeString}`,
};
} else if (isClarityAbiStringUtf8(type)) {
return {
type: 'input',
name,
message: `Enter value for function argument "${name}" of type ${typeString}`,
};
} else if (isClarityAbiResponse(type)) {
throw new Error(`Contract function contains unsupported Clarity ABI type: ${typeString}`);
} else if (isClarityAbiOptional(type)) {
throw new Error(`Contract function contains unsupported Clarity ABI type: ${typeString}`);
return {
type: 'input',
name,
message: `Enter value for function argument "${name}" of type ${typeString}`,
};
} else if (isClarityAbiTuple(type)) {
throw new Error(`Contract function contains unsupported Clarity ABI type: ${typeString}`);
} else if (isClarityAbiList(type)) {
Expand Down Expand Up @@ -881,10 +902,16 @@ export function answerToClarityValue(answer: any, arg: ClarityFunctionArg): Clar
}
} else if (isClarityAbiBuffer(type)) {
return bufferCVFromString(answer);
} else if (isClarityAbiStringAscii(type)) {
return stringAsciiCV(answer);
} else if (isClarityAbiStringUtf8(type)) {
return stringUtf8CV(answer);
} else if (isClarityAbiResponse(type)) {
throw new Error(`Contract function contains unsupported Clarity ABI type: ${typeString}`);
} else if (isClarityAbiOptional(type)) {
throw new Error(`Contract function contains unsupported Clarity ABI type: ${typeString}`);
return someCV(
answerToClarityValue(answer, { name: arg.name, type: type.optional } as ClarityFunctionArg)
);
} else if (isClarityAbiTuple(type)) {
throw new Error(`Contract function contains unsupported Clarity ABI type: ${typeString}`);
} else if (isClarityAbiList(type)) {
Expand Down
82 changes: 82 additions & 0 deletions packages/cli/tests/abi/test-abi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
"functions": [
{
"name": "test-func-string-ascii-argument",
"access": "public",
"args": [
{
"name": "currency",
"type": {
"string-ascii": {
"length": 16
}
}
}
],
"outputs": { "type": { "response": { "ok": "bool", "error": "none" } } }
},
{
"name": "test-func-string-utf8-argument",
"access": "public",
"args": [
{
"name": "msg",
"type": {
"string-utf8": {
"length": 16
}
}
}
],
"outputs": { "type": { "response": { "ok": "bool", "error": "none" } } }
},
{
"name": "test-func-optional-argument",
"access": "public",
"args": [
{
"name": "optional",
"type": {
"optional": {
"string-utf8": {
"length": 200
}
}
}
}
],
"outputs": { "type": { "response": { "ok": "bool", "error": "none" } } }
},
{
"name": "test-func-primitive-argument",
"access": "public",
"args": [
{
"name": "amount",
"type": "uint128"
},
{
"name": "address",
"type": "principal"
},
{
"name": "exists",
"type": "bool"
}
],
"outputs": { "type": { "response": { "ok": "bool", "error": "none" } } }
},
{
"name": "test-func-buffer-argument",
"access": "public",
"args": [
{ "name": "bufferArg", "type": { "buffer": { "length": 20 } } }
],
"outputs": { "type": { "response": { "ok": "bool", "error": "none" } } }
}
],
"variables": [],
"maps": [],
"fungible_tokens": [],
"non_fungible_tokens": []
}
158 changes: 157 additions & 1 deletion packages/cli/tests/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,16 @@ import { getNetwork, CLINetworkAdapter, CLI_NETWORK_OPTS } from '../src/network'
import { CLI_CONFIG_TYPE } from '../src/argparse';

import * as fixtures from './fixtures/cli.fixture';
import inquirer from 'inquirer';
import {ClarityAbi} from '@stacks/transactions';
import {readFileSync} from 'fs';
import path from 'path';
import fetchMock from 'jest-fetch-mock';

const { addressConvert } = testables as any;
const TEST_ABI: ClarityAbi = JSON.parse(readFileSync(path.join(__dirname, './abi/test-abi.json')).toString());
jest.mock('inquirer');

const { addressConvert, contractFunctionCall } = testables as any;

const mainnetNetwork = new CLINetworkAdapter(
getNetwork({} as CLI_CONFIG_TYPE, false),
Expand All @@ -23,3 +31,151 @@ describe('convert_address', () => {
expect(JSON.parse(result)).toEqual(expectedResult);
});
});

describe('Contract function call', () => {
test('Should accept string-ascii clarity type argument', async () => {
const contractAddress = 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6';
const contractName = 'test-contract-name';
const functionName = 'test-func-string-ascii-argument';
const fee = 200;
const nonce = 0;
const privateKey = 'cb3df38053d132895220b9ce471f6b676db5b9bf0b4adefb55f2118ece2478df01';
const args = [
contractAddress,
contractName,
functionName,
fee,
nonce,
privateKey
];
const contractInputArg = { currency: 'USD' };

// @ts-ignore
inquirer.prompt = jest.fn().mockResolvedValue(contractInputArg);

fetchMock.once(JSON.stringify(TEST_ABI)).once('success');

const txid = '0x6c764e276b500babdac6cec159667f4b68938d31eee82419473a418222af7d5d';
const result = await contractFunctionCall(testnetNetwork, args);

expect(result.txid).toEqual(txid);
});

test('Should accept string-utf8 clarity type argument', async () => {
const contractAddress = 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6';
const contractName = 'test-contract-name';
const functionName = 'test-func-string-utf8-argument';
const fee = 210;
const nonce = 1;
const privateKey = 'cb3df38053d132895220b9ce471f6b676db5b9bf0b4adefb55f2118ece2478df01';
const args = [
contractAddress,
contractName,
functionName,
fee,
nonce,
privateKey
];
const contractInputArg = { msg: 'plain text' };

// @ts-ignore
inquirer.prompt = jest.fn().mockResolvedValue(contractInputArg);

fetchMock.once(JSON.stringify(TEST_ABI)).once('success');

const txid = '0x97f41dfa44a5833acd9ca30ffe31d7137623c0e31a5c6467daeed8e61a03f51c';
const result = await contractFunctionCall(testnetNetwork, args);

expect(result.txid).toEqual(txid);
});

test('Should accept optional clarity type argument', async () => {
const contractAddress = 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6';
const contractName = 'test-contract-name';
const functionName = 'test-func-optional-argument';
const fee = 220;
const nonce = 2;
const privateKey = 'cb3df38053d132895220b9ce471f6b676db5b9bf0b4adefb55f2118ece2478df01';
const args = [
contractAddress,
contractName,
functionName,
fee,
nonce,
privateKey
];
const contractInputArg = { optional: 'optional string-utf8 string' };

// @ts-ignore
inquirer.prompt = jest.fn().mockResolvedValue(contractInputArg);

fetchMock.once(JSON.stringify(TEST_ABI)).once('success');

const txid = '0x5fc468f21345c5ecaf1c007fce9630d9a79ec1945ed8652cc3c42fb542e35fe2';
const result = await contractFunctionCall(testnetNetwork, args);

expect(result.txid).toEqual(txid);
});

test('Should accept primitive clarity type arguments', async () => {
const contractAddress = 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6';
const contractName = 'test-contract-name';
const functionName = 'test-func-primitive-argument';
const fee = 230;
const nonce = 3;
const privateKey = 'cb3df38053d132895220b9ce471f6b676db5b9bf0b4adefb55f2118ece2478df01';
const args = [
contractAddress,
contractName,
functionName,
fee,
nonce,
privateKey
];
const contractInputArg = {
amount: 1000,
address: 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6',
exists: false,
};

// @ts-ignore
inquirer.prompt = jest.fn().mockResolvedValue(contractInputArg);

fetchMock.once(JSON.stringify(TEST_ABI)).once('success');

const txid = '0x94b1cfab79555b8c6725f19e4fcd6268934d905578a3e8ef7a1e542b931d3676';
const result = await contractFunctionCall(testnetNetwork, args);

expect(result.txid).toEqual(txid);
});

test('Should accept buffer clarity type argument', async () => {
const contractAddress = 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6';
const contractName = 'test-contract-name';
const functionName = 'test-func-buffer-argument';
const fee = 240;
const nonce = 4;
const privateKey = 'cb3df38053d132895220b9ce471f6b676db5b9bf0b4adefb55f2118ece2478df01';
const args = [
contractAddress,
contractName,
functionName,
fee,
nonce,
privateKey
];
const contractInputArg = {
bufferArg: 'string buffer'
};

// @ts-ignore
inquirer.prompt = jest.fn().mockResolvedValue(contractInputArg);

fetchMock.once(JSON.stringify(TEST_ABI)).once('success');

const txid = '0x6b6cd5bfb44c46a68090f0c5f659e9cc02518eafab67b0b740e1e77a55bbf284';
const result = await contractFunctionCall(testnetNetwork, args);

expect(result.txid).toEqual(txid);
});
});

1 comment on commit a02665a

@vercel
Copy link

@vercel vercel bot commented on a02665a Sep 7, 2021

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.