Skip to content

AmeNetwork/ame-component

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Ame Component

Introduction

Ame Component is an onchain component based on EIP-7654 that decomposes complex onchain processes into independent, composable, reusable, and highly adaptable functional units.

  • getMethods Get method names based on request method type.
  • getMethodReqAndRes Get the data types of request parameters and return parameters based on the requested method name.
  • getMethodInstruction Get the instruction of method.
  • get Request the contract to retrieve records.
  • post Request the contract to create a new record.
  • put Request the contract to update a record.
  • options Supported request method types.

Component Example

This is an example of profiles component.

  1. Users can create new profiles by calling the createUser method of post.
  2. Users can query profiles by calling the getUser method of get.
  3. Users can change the name by calling the updateUserName method of put.
// SPDX-License-Identifier: CC0-1.0
pragma solidity >=0.8.0;
import "./Types.sol";
import "./IComponent.sol";

contract Component is IComponent {
    //@dev Types contains all data types in solidity
    mapping(string => Types.Type[]) methodRequests;
    mapping(string => Types.Type[]) methodResponses;
    mapping(MethodTypes => string[]) methods;
    mapping(string => string) instructions;

    //@dev define the data type of this component
    struct Profiles {
        string name;
        uint256 age;
    }
    mapping(address => Profiles) users;

    constructor() {
        Types.Type[] memory getReqArray = new Types.Type[](1);
        getReqArray[0] = Types.Type.ADDRESS;
        Types.Type[] memory dataTypeArray = new Types.Type[](2);
        dataTypeArray[0] = Types.Type.STRING;
        dataTypeArray[1] = Types.Type.UINT256;
        Types.Type[] memory putReqArray = new Types.Type[](2);
        putReqArray[0] = Types.Type.ADDRESS;
        putReqArray[1] = Types.Type.STRING;
        // @dev initialize get, post, put request parameter data types and response data types
        setMethod(
            "getUser",
            MethodTypes.GET,
            getReqArray,
            dataTypeArray,
            "get user profiles"
        );
        setMethod(
            "createUser",
            MethodTypes.POST,
            dataTypeArray,
            dataTypeArray,
            "Create user profiles"
        );
        setMethod(
            "updateUserName",
            MethodTypes.PUT,
            putReqArray,
            new Types.Type[](0),
            "Update user information"
        );
    }

    function get(string memory _methodName, bytes memory _methodReq)
        public
        view
        returns (bytes memory)
    {
        if (compareStrings(_methodName, "getUser")) {
            address user = abi.decode(_methodReq, (address));
            bytes memory userData = abi.encode(
                users[user].name,
                users[user].age
            );
            return userData;
        } else {
            return abi.encode("");
        }
    }

    function post(string memory _methodName, bytes memory _methodReq)
        public
        payable
        returns (bytes memory)
    {
        if (compareStrings(_methodName, "createUser")) {
            (string memory name, uint256 age) = abi.decode(
                _methodReq,
                (string, uint256)
            );
            users[msg.sender] = Profiles(name, age);
            bytes memory resBytes = abi.encode(name, age);
            emit Response(resBytes);
            return resBytes;
        }
        return abi.encode("");
    }

    function put(string memory _methodName, bytes memory _methodReq)
        public
        payable
        returns (bytes memory)
    {
        if (compareStrings(_methodName, "updateUserName")) {
            (address userAddress, string memory name) = abi.decode(
                _methodReq,
                (address, string)
            );
            require(userAddress == msg.sender);
            users[userAddress].name = name;
        }
        return abi.encode("");
    }

    function options() public pure returns (MethodTypes[] memory) {
        MethodTypes[] memory methodTypes = new MethodTypes[](3);
        methodTypes[0] = MethodTypes.GET;
        methodTypes[1] = MethodTypes.POST;
        methodTypes[2] = MethodTypes.PUT;
        return methodTypes;
    }

    function setMethod(
        string memory _methodName,
        MethodTypes _methodType,
        Types.Type[] memory _methodReq,
        Types.Type[] memory _methodRes,
        string memory _instruction
    ) private {
        methods[_methodType].push(_methodName);
        methodRequests[_methodName] = _methodReq;
        methodResponses[_methodName] = _methodRes;
        instructions[_methodName] = _instruction;
    }

    function getMethodReqAndRes(string memory _methodName)
        public
        view
        returns (Types.Type[] memory, Types.Type[] memory)
    {
        return (methodRequests[_methodName], methodResponses[_methodName]);
    }

    function getMethods(MethodTypes _methodTypes)
        public
        view
        returns (string[] memory)
    {
        return methods[_methodTypes];
    }

    function getMethodInstruction(string memory _methodName)
        public
        view
        returns (string memory)
    {
        return instructions[_methodName];
    }

    //@dev compares two strings for equality
    function compareStrings(string memory _a, string memory _b)
        private
        pure
        returns (bool)
    {
        return
            keccak256(abi.encodePacked(_a)) == keccak256(abi.encodePacked(_b));
    }
}

Developer FAQs

How to customize response status code?

The return value type of each request method is bytes, so you can customize the response status code and other response information according to your needs.

How to disable a request method?

You can add a mapping(string=>bool) to save the request method name and available state. After the application is upgraded, manually set the available state of the disabled request method to false. And check the state every time a request is processed.

Why is there no delete method type?

The data in smart contracts is public, and deleting data is an inefficient operation. In order to facilitate data management and retrieval, you can add a mapping to save the valid state of the data, and add a put request method to set valid and invalid data. And only return valid data in get method.

How to dynamically add new request methods?

Before deploying the contract, you can add a mapping (address=>bool) to record the external contract permissions. When the application adds new functions, give the v2 contract write permissions and add new request methods to the v2 contract.

How to encode request parameters and decode response?

Use getMethodReqAndRes to get the data type of the method request parameters and the data type of the response value. Then use some js library to encode and decode it.
For example:

var reqDataEncode = web3.eth.abi.encodeParameters(
  ["string", "uint256"],
  ["alice", "1"]
);

var resDataDecode = web3.eth.abi.decodeParameters(
  ["string", "uint256"],
  reqDataEncode
);

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published