Skip to content

Support Klaytn Network #4191

@JamesSmartCell

Description

@JamesSmartCell

Mainnet: Klaytn Cypress

ID: 8217
RPC: https://public-node-api.klaytnapi.com/v1/cypress
Symbol: KLAY
Block Explorer URL (For Transaction Detail): https://scope.klaytn.com/tx/
SVG Asset: https://zpl.io/jZEjrkx
Network Style Colour: #FE3300
CoinGecko API ID: klay-token (for both chain price and Token price queries).

Testnet: Klaytn Baobab

ID: 1001
RPC: https://api.baobab.klaytn.net:8651
Symbol: KLAY
Block Explorer URL (For Transaction Detail): https://baobab.scope.klaytn.com/tx/
SVG Asset:

<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
    <g fill-rule="nonzero" fill="none">
        <path d="M13.132 1.852A7.97 7.97 0 0 0 8.287 0L4.205 10.778l8.927-8.926z" fill="#B2C6D8" fill-rule="nonzero"/>
        <path d="M14.15 2.871 9.023 8l5.126 5.124a8.004 8.004 0 0 0 0-10.253z" fill="#B2C6D8" fill-rule="nonzero"/>
        <path d="M6.28 1.23.016 7.494c-.129 2.013.51 4 1.785 5.563L6.281 1.23z" fill="#B2C6D8" fill-rule="nonzero"/>
        <path d="m8.004 9.017-5.126 5.126a8.004 8.004 0 0 0 10.252 0L8.004 9.017z" fill="#B2C6D8" fill-rule="nonzero"/>
    </g>
</svg>

Network Style Colour: #313557

This will give you basic support. However for full support you need to integrate Covalent. I have gone for a minimal approach to begin with, converting the Covalent API data into Etherscan Transfer event objects so they can be fed into your existing transfer event handling without too much modification. Currently I am supporting only FT & NFT Transfers and Contract constructor detection. We can also easily add handling for 'Approve', and other standard contract events for ERC20, 721 and 1155 at a later date.

Here's the Covalent API endpoint you will be using:

https://api.covalenthq.com/v1/8217/address/<ACCOUNT ADDRESS>/transactions_v2/?key=<API Key>&page-number=0&page-size=800

This will return:

   "data": {
        "address": "<ACCOUNT ADDRESS>",
        "updated_at": "2022-03-29T22:12:45.829682424Z",
        "next_update_at": "2022-03-29T22:17:45.829682674Z",
        "quote_currency": "USD",
        "chain_id": 8217,
        "items": [ <Transactions> ],
        }

Each element looks like this - eg, an ERC721 Transfer event (Mint in this case):

{
                "block_signed_at": "2022-03-29T22:12:43Z",
                "block_height": 86708074,
                "tx_hash": "0xbc1f9892f2304e8d64e0f3bd0a7ece0a4c4f39169460b1ea87beb28820f30caa",
                "tx_offset": 10,
                "successful": true,
                "from_address": "0x424d0c6ed9addc9e80e81dbfe6e26911e4a25c8c",
                "from_address_label": null,
                "to_address": "0xb6fc2d963979a78fc7351695fcb829bc3c8b5b29",
                "to_address_label": null,
                "value": "0",
                "value_quote": 0.0,
                "gas_offered": 356057,
                "gas_spent": 222437,
                "gas_price": 25000000000,
                "gas_quote": 0.006758514791113139,
                "gas_quote_rate": 1.215358018875122,
                "log_events": [  <--- Can be multiple of these especially if TX has has chained events
                    {
                        "block_signed_at": "2022-03-29T22:12:43Z",
                        "block_height": 86708074,
                        "tx_offset": 10,
                        "log_offset": 4,
                        "tx_hash": "0xbc1f9892f2304e8d64e0f3bd0a7ece0a4c4f39169460b1ea87beb28820f30caa",
                        "raw_log_topics": [
                            "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
                            "0x0000000000000000000000000000000000000000000000000000000000000000",
                            "0x00000000000000000000000069dca5507a9adf8576b5cae4f1b332c75e5e5f01",
                            "0x00000000000000000000000000000000000000000000000000000000000026d9"
                        ],
                        "sender_contract_decimals": 0,
                        "sender_name": "klay pixelmfers",
                        "sender_contract_ticker_symbol": "KPXMF",
                        "sender_address": "0xb6fc2d963979a78fc7351695fcb829bc3c8b5b29",
                        "sender_address_label": null,
                        "sender_logo_url": "https://logos.covalenthq.com/tokens/8217/0xb6fc2d963979a78fc7351695fcb829bc3c8b5b29.png",
                        "raw_log_data": null,
                        "decoded": {
                            "name": "Transfer",
                            "signature": "Transfer(indexed address from, indexed address to, uint256 value)",
                            "params": [
                                {
                                    "name": "from",
                                    "type": "address",
                                    "indexed": true,
                                    "decoded": true,
                                    "value": "0x0000000000000000000000000000000000000000"
                                },
                                {
                                    "name": "to",
                                    "type": "address",
                                    "indexed": true,
                                    "decoded": true,
                                    "value": "0x69dca5507a9adf8576b5cae4f1b332c75e5e5f01"
                                },
                                {
                                    "name": "value",
                                    "type": "uint256",
                                    "indexed": false,
                                    "decoded": false,
                                    "value": null
                                }
                            ]
                        }
                    }
                ]
            },

I have handled this by converting it into a stream of EtherscanEvent which matches a single element from the Etherscan transfers API:

First handle the params:

        public Map<String, Param> getParams() throws Exception
        {
            Map<String, Param> params = new HashMap<>();
            if (decoded == null || decoded.params == null) return params;

            for (int index = 0; index < decoded.params.length; index++)
            {
                String rawLogValue = (index + 1) < raw_log_topics.length ? raw_log_topics[index + 1] : ""; // <-- See raw_log_topics above, first entry is event hash, then the params
                LogParam lp = decoded.params[index];
                Param param = new Param();
                param.type = lp.type;
                String rawValue = TextUtils.isEmpty(lp.value) || lp.value.equals("null") ? rawLogValue : lp.value; // <-- value can be either in the 'params' from the API return or in the raw_log_topics array
                if (lp.type.startsWith("uint") || lp.type.startsWith("int"))
                {
                    param.valueBI = rawValue.startsWith("0x") ? Numeric.toBigInt(rawValue) : new BigInteger(rawValue);
                    param.value = "";
                }
                else
                {
                    param.value = rawValue;
                }

                params.put(lp.name, param);
            }

            return params;
        }

Then do full conversion to 'EtherscanEvent':

    private EtherscanEvent getEtherscanTransferEvent(LogEvent logEvent) throws Exception
    {
        if (logEvent == null || logEvent.decoded == null || !logEvent.decoded.name.equals("Transfer")) return null; // <--- Currently only handle "Transfer" events to reduce code changes

        EtherscanEvent ev = new EtherscanEvent();
        ev.tokenDecimal = String.valueOf(logEvent.sender_contract_decimals);
        ev.timeStamp = format.parse(block_signed_at).getTime() / 1000;
        ev.hash = tx_hash;
        ev.nonce = 0;   // <-- Covalent doesn't provide TX nonce
        ev.tokenName = logEvent.sender_name;
        ev.tokenSymbol = logEvent.sender_contract_ticker_symbol;
        ev.contractAddress = logEvent.sender_address;
        ev.blockNumber = block_height;

        Map<String, Param> logParams = logEvent.getParams();

        ev.from = logParams.get("from").value;
        ev.to = logParams.get("to").value;

        logParams.remove("from");
        logParams.remove("to");

        if (logEvent.sender_contract_decimals == 0) //< --- Is an NFT transfer
        {
            //get TokenId
            ev.tokenID = logParams.values().iterator().next().valueBI.toString();
        }
        else
        {
            ev.value = logParams.values().iterator().next().valueBI.toString();  // <-- Is ERC20 Transfer
        }

        ev.gasUsed = gas_spent;
        ev.gasPrice = gas_price;
        ev.gas = String.valueOf(gas_offered);

        return ev;
    }

Where EtherscanEvent Looks like this:

public class EtherscanEvent
{
    public String blockNumber;
    public long timeStamp;
    public String hash;
    public int nonce;
    String blockHash;
    public String from;
    public String contractAddress;
    public String to;
    public String tokenID;
    public String value;
    public String tokenName;
    public String tokenSymbol;
    public String tokenDecimal;
    public String input;
    String gas;
    String gasPrice;
    String gasUsed;
}

Which is the return from Etherscan Transfer API.

Using something like the above you can feed the events (which match the objects returned from the Etherscan transfer API) directly into your existing code.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions