How to Integrate Your Smart Contract with kOS?

In this page, we are going to use a simple smart contract as an example.

Smart Contract (before)

Here is the example of base Solidity smart contract, which is the contract that we want to register on the KRNL Platform.

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

contract UnprotectedSampleContract {

    string message = "hello";

    function unprotectedFunction(string memory input) external {
        message = input;
    }

    function readMessage() external view returns (string memory) {
        return message;
    }
}

Extension Part for Upgrading Your Smart Contract (KRNL.sol)

Below here is the block of Solidity which you need to add into your smart contract. It allows your smart contract to be able to use the KRNL Operating System, kOS.

The directory of your project would look like the picture.

KRNL.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";

// Struct to group the parameters
struct KrnlPayload {
    bytes auth;
    bytes kernelResponses;
    bytes kernelParams;
}

struct KernelParameter {
    uint8 resolverType;
    bytes parameters;
    string err;
}

struct KernelResponse {
    uint256 kernelId;
    bytes result;
    string err;
}

// Draft Version
contract KRNL is Ownable {
    error UnauthorizedTransaction();

    address public tokenAuthorityPublicKey;
    mapping(bytes => bool) public executed;

    modifier onlyAuthorized(
        KrnlPayload memory krnlPayload,
        bytes memory params
    ) {
        if (!_isAuthorized(krnlPayload, params)) {
            revert UnauthorizedTransaction();
        }

        _;
    }

    constructor(address _tokenAuthorityPublicKey) Ownable(msg.sender) {
        tokenAuthorityPublicKey = _tokenAuthorityPublicKey;
    }

    function setTokenAuthorityPublicKey(
        address _tokenAuthorityPublicKey
    ) external onlyOwner {
        tokenAuthorityPublicKey = _tokenAuthorityPublicKey;
    }

    function _isAuthorized(
        KrnlPayload memory payload,
        bytes memory functionParams
    ) private view returns (bool) {
        
        (
            bytes memory kernelResponseSignature,
            bytes32 kernelParamObjectDigest,
            bytes memory signatureToken,
            uint256 nonce,
            bool finalOpinion
        ) = abi.decode(
                payload.auth,
                (bytes, bytes32, bytes, uint256, bool)
            );

        if (finalOpinion == false) {
            revert("Final opinion reverted");
        }

        bytes32 kernelResponsesDigest = keccak256(
            abi.encodePacked(payload.kernelResponses, msg.sender)
        );

        address recoveredAddress = ECDSA.recover(
            kernelResponsesDigest,
            kernelResponseSignature
        );

        if (recoveredAddress != tokenAuthorityPublicKey) {
            revert("Invalid signature for kernel responses");
        }

        bytes32 _kernelParamsDigest = keccak256(
            abi.encodePacked(payload.kernelParams, msg.sender)
        );

        bytes32 functionParamsDigest = keccak256(functionParams);

        if (_kernelParamsDigest != kernelParamObjectDigest) {
            revert("Invalid kernel params digest");
        }

        bytes32 dataDigest = keccak256(
            abi.encodePacked(
                functionParamsDigest,
                kernelParamObjectDigest,
                msg.sender,
                nonce,
                finalOpinion
            )
        );

        recoveredAddress = ECDSA.recover(dataDigest, signatureToken);
        if (recoveredAddress != tokenAuthorityPublicKey) {
            revert("Invalid signature for function call");
        }

        // executed[signatureToken] = true;
        return true;
    }
}

Upgraded Smart Contract (after)

Instead of having a long chuck of codes in the smart contract, this version requires the inheritance of KRNL.sol into your smart contract. As well as the necessary lines of code for decoding kernel(s) responses.

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {KRNL, KrnlPayload, KernelParameter, KernelResponse} from "./KRNL.sol";
// ===============================
// This smart contract is specifically built to be compatible with kernel 337 as an example.
// If you are using this source code as a project template, make sure you change the following lines.
// Line 35
// Line 37
// ===============================
contract Sample is KRNL {
    // Token Authority public key as a constructor
    constructor(address _tokenAuthorityPublicKey) KRNL(_tokenAuthorityPublicKey) {}

    // Initial value of message when this contract is being created
    string message = "hello";

    // Results from kernel will be emitted through this event
    event Broadcast(address sender, uint256 score, string message);

    // Protected function
    function protectedFunction(
        KrnlPayload memory krnlPayload,
        string memory input
    )
        external
        onlyAuthorized(krnlPayload, abi.encode(input))
    {
        
        // Decode response from kernel
        KernelResponse[] memory kernelResponses = abi.decode(krnlPayload.kernelResponses, (KernelResponse[]));
        uint256 score;
        for (uint i; i < kernelResponses.length; i ++) {
            // Change the line below to match with your selected kernel(s)
            if (kernelResponses[i].kernelId == 337) {
                // Change the code below to match with the return data type from this kernel
                score = abi.decode(kernelResponses[i].result, (uint256));
            }
            // ===============================
            // If you have more than 1 kernel, you can add more conditions
            // if (kernelResponses[i].kernelId == REPLACE_WITH_KERNEL_ID) {
            //     // Change the code below to match with the return data type from this kernel
            //     foo = abi.decode(kernelResponses[i].result, (bool));
            // }
            // ===============================
        }

        // Write new message
        message = input;

        // Emitting an event
        emit Broadcast(msg.sender, score, input);
    }

    // Read message from contract
    function readMessage() external view returns (string memory) {
        return message;
    }
}

// ===============================
// Simple version of smart contract example. It does not contain the decoding part.
// No response from kernel is shown.
// No event is emitted during the transaction.
// ===============================

// contract Sample is KRNL {
//     constructor(address _tokenAuthorityPublicKey) KRNL(_tokenAuthorityPublicKey) {}

//     string message = "hello";

//     function protectedFunction(
//         KrnlPayload memory krnlPayload,
//         string memory input
//     )
//         external
//         onlyAuthorized(krnlPayload, abi.encode(input))
//     {
//         message = input;
//     }

//     function readMessage() external view returns (string memory) {
//         return message;
//     }
// }

Deployment

At this point, you can now deploy your upgraded smart contract.

Last updated

Was this helpful?