How to create a new Verifiable Credential Solidity contract
Basic Contract Template
A new contract must extend base contractDynamicAssertion, which is extended by many of our VC contracts,e.g. A1, TokenHoldingAmount, PlatformUser. A very simplified version that extends the base contract is given below:
pragmasolidity ^0.8.8;import'./libraries/AssertionLogic.sol';import'./libraries/Identities.sol';import'./DynamicAssertion.sol';contractSampleContractisDynamicAssertion {functionexecute(Identity[] memory identities,string[] memory secrets,// Immutable secret values set at deployment, e.g., API authorization token.bytesmemory params // Additional caller-supplied params, use `abi.decode` to extract values. )publicoverridereturns (stringmemory,// Description of the assertion.stringmemory,// Assertion type.string[] memory,// Assertions built using composite conditions.stringmemory,// JSON schema URL for VC result validation.bool// Assertion result. ) {// Assertion description that decided by you.stringmemory description ='Some description';// Assertion type name that decided by you.stringmemory assertion_type ='Some assertion type';// The schema_url is the JSON schema that use to verify your result VC. schema_url ='https://raw.githubusercontent.com/litentry/vc-jsonschema/main/dist/schemas/xxx/xxx.json';// Calculate the result value for your business.bool result =true;// Assemble the assertion condition and clause what you need. AssertionLogic.CompositeCondition memory cc = AssertionLogic .CompositeCondition(new AssertionLogic.Condition[](1),true); AssertionLogic.andOp( cc,0,'$some_key', AssertionLogic.Op.Equal,'true' );string[] memory assertions =newstring[](1); assertions[0] = AssertionLogic.toString(cc);// The returned result will be use by parachain to assemble VC result and return to client.return (description, assertion_type, assertions, schema_url, result); }}
How to Call HTTP API in Contract?
The following template demonstrates calling an HTTP API:
import"./libraries/Http.sol";stringmemory url =string( abi.encodePacked("https://example.com/api?account=", account ));// Use the JSON pointer to retrieve value from the API response.stringmemory jsonPointer ="/success";HttpHeader[] memory headers =new HttpHeader[](1);// Use value from secrets as Authorization token.headers[0] =HttpHeader("Authorization", secrets[0]);// The `success = true` means call `Http.GetBool` successfully.// The result is the value that get using JSON pointer from the API response.(bool success,bool value) = Http.GetBool( url, jsonPointer, headers);if (success) {// The value can be use here.}
Supported HTTP Functions
The following HTTP functions are available for use:
Http.GetI64(string memory url, string memory jsonPointer, HttpHeader[] memory headers): Executes a GET request and retrieves an I64 value from the response using a JSON pointer.
Http.GetBool(string memory url, string memory jsonPointer, HttpHeader[] memory headers): Executes a GET request and retrieves an Bool value from the response using a JSON pointer.
Http.GetString(string memory url, string memory jsonPointer, HttpHeader[] memory headers): Executes a GET request and retrieves an String value from the response using a JSON pointer.
Http.Get(string memory url, HttpHeader[] memory headers): Executes a GET request and returns a whole response JSON - it can be used later to get data using json pointers. Compared to Get* functions this is the method to get multiple values from single response.
Similarly, the following POST methods are supported:
Http.PostI64, Http.PostBool, Http.PostString, and Http.Post: These behave the same as their GET counterparts but use POST requests.
Processing Complex API Responses
Handling Decimal Values
Example response:
{"value":"123456789.123456789"}
Example code:
import"./libraries/Http.sol";import"./libraries/Utils.sol";stringmemory url =string( abi.encodePacked("https://example.com/api?account=", account ));stringmemory jsonPointer ="/value";HttpHeader[] memory headers =new HttpHeader[](0);(bool success,stringmemory value) = Http.GetString( url, jsonPointer, headers);if (getSuccess) {// The 9 is decimals of string number, solidity does not support decimal, so need multiply decimals to convert it to an integer. (bool parseSuccess,uint256 parsedValue) = Utils.parseDecimal(getResult,9);if (parseSuccess) {// The parsedValue can be use here. }}
Handling Multiple Values from API Response
Example response:
[ { count:1 }, { count:2 }, ...]
Example code:
import "./libraries/Http.sol";import "./libraries/Json.sol";string memory url = string( abi.encodePacked("https://example.com/api?account=", account ));HttpHeader[] memory headers = new HttpHeader[](0);// Firstly, all the http API.(bool success, string memory value) = Http.Get( url, headers);int64 total = 0;if (getSuccess) {// Secondary, calculate the array length of the return API response. (bool arrayLenSuccess, int64 arrayLen) = Json.getArrayLen( value,""// The empty pointer means from root. ); if (arrayLenSuccess) {// Tertiary, get the count value of each array item and sum. for (uint256 i = 0; i < uint256(int256(arrayLen)); i++) { ( bool getI64Success, i64 count ) = Json.getI64( tokensResponse, string( abi.encodePacked("/", Strings.toString(i),"/count" ) ) ); if (getI64Success) { total += count; } } }}
This method logs messages into the contract’s call context. The log messages will be returned to the client if the return_log param is true when calling the contract.
Example Code:
import"./libraries/Logging.sol";Logging.debug("This is a debug message.");Logging.info("This is a info message.");Logging.warn("This is a warn message.");Logging.fatal("This is a fatal message.");Logging.error("This is a error message.");
Example parachain CLI Usage (note this approach requires you have access to the running tee-worker built with dev feature):
This method is specifically for debugging while running contracts in a Hardhat environment. It won’t log messages once the contract is deployed to an EVM. For more details, refer to Hardhat’s Debugging Documentation.
Example Code:
import"hardhat/console.sol";console.log("This is a log message","msg1","msg2");