{"id":19747486,"url":"https://github.com/phantasma-io/phantasma-go","last_synced_at":"2025-02-28T00:20:15.571Z","repository":{"id":237344285,"uuid":"770341450","full_name":"phantasma-io/phantasma-go","owner":"phantasma-io","description":"A Go SDK for the Phantasma blockchain","archived":false,"fork":false,"pushed_at":"2025-02-22T00:32:51.000Z","size":414,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-22T01:24:39.743Z","etag":null,"topics":["blockchain","crypto","cryptocurrency","go","layer1","phantasma","phantasmachain","phantasmaio","sdk","smartnft","smartnfts"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/phantasma-io.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-11T11:38:57.000Z","updated_at":"2024-08-03T17:17:44.000Z","dependencies_parsed_at":"2024-07-20T00:29:01.757Z","dependency_job_id":"4a64e08a-9d9e-4b23-946a-0c149425fdc5","html_url":"https://github.com/phantasma-io/phantasma-go","commit_stats":null,"previous_names":["phantasma-io/phantasma-go"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phantasma-io%2Fphantasma-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phantasma-io%2Fphantasma-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phantasma-io%2Fphantasma-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phantasma-io%2Fphantasma-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/phantasma-io","download_url":"https://codeload.github.com/phantasma-io/phantasma-go/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241078478,"owners_count":19905865,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blockchain","crypto","cryptocurrency","go","layer1","phantasma","phantasmachain","phantasmaio","sdk","smartnft","smartnfts"],"created_at":"2024-11-12T02:17:59.520Z","updated_at":"2025-02-28T00:20:15.529Z","avatar_url":"https://github.com/phantasma-io.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003cimg src=\"./.github/phantasma-go.jpg\" width=\"300px\" alt=\"logo\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cb\u003eGo\u003c/b\u003e SDK for the \u003ca href=\"https://phantasma.io\"\u003ePhantasma\u003c/a\u003e blockchain.\n\u003c/p\u003e\n\n\u003chr /\u003e\n\n![License](https://img.shields.io/github/license/phantasma-io/phantasma-go.svg?style=popout)\n\n# Overview\n\nThis project aims to be an easy to use SDK for the Phantasma blockchain.\n\n# Documentation\n\n## Installation\n\nPhantasmaGo is distributed as a library that includes all the functionality provided.\n\n```\ngo get -u github.com/phantasma-io/phantasma-go\n```\n\n## Getting started\nTo start interacting with Phantasma blockchain you need to choose network you are planning to use (mainnet or testnet) and create corresponding RPC client.\n\nCreation of testnet RPC client:\n```\nclient = rpc.NewRPCTestnet()\n```\n\nCreation of mainnet RPC client:\n```\nclient = rpc.NewRPCMainnet()\n```\n\nTo create a new key pair structure from private key in WIF format use following code:\n```\nkeyPair, err := cryptography.FromWIF(\"put WIF here\")\nif err != nil {\n    panic(\"Creating keyPair failed!\")\n}\n```\n\nTo get detailed description of tokens deployed on the chain you can use following code:\n\n```\nvar chainTokens []response.TokenResult\n\nfunc getChainToken(symbol string) response.TokenResult {\n    for _, t := range chainTokens {\n        if t.Symbol == symbol {\n            return t\n        }\n    }\n\n    panic(\"Token not found\")\n}\n\nchainTokens, _ = client.GetTokens(false)\n```\n\nThis will allow you to get token characteristics this way:\n\n```\nt := getChainToken(\"SOUL\")\nif t.IsFungible() {\n    fmt.Println(\"Token SOUL is fungible\")\n}\n```\n\nCode samples in the following sections of this documentation use `client` and `keyPair` structures and method `getChainToken` which should be initialized in advance.\n\n## Script Builder\n\nBuilding a script is the most important part of interacting with the Phantasma blockchain. Without a propper script, the Phantasma blockchain will not know what you are trying to do.\n\nThese functions, `CallContract` and `CallInterop`, are your bread and butter for creating new scripts.\n\n```\nfunc (s ScriptBuilder) CallContract(contractName, method string, args ...interface{})\n```\n\n```\nfunc (s ScriptBuilder) CallInterop(method string, args ...interface{})\n```\n\nYou can find out all the diffrent `CallInterop` functions below.\n\nFor `CallContract`, you will have to look through the ABI's of all the diffrent smart contracts currently deployed on the Phantasma 'mainnet': [Link Here](https://explorer.phantasma.info/en/nexus?tab=contracts). To see all methods of a contract, for example `stake`, you can check it with explorer: [Link Here](https://explorer.phantasma.info/en/contract?id=stake\u0026tab=methods).\n\n### Examples\n\nFollowing code generates script to transfer `tokenAmount` amount of token `tokenSymbol` from wallet `from` to wallet `to`\n```\nfrom := \"put sender address here\" // Phantasma address, starting with capital 'P'\nto := \"put recepient address here\" // Phantasma address, starting with capital 'P'\ntokenAmount := big.NewInt(1000000000) // Token amount in the form of big integer\ntokenSymbol := \"SOUL\"\n\nsb := scriptbuilder.BeginScript()\nscript := sb.CallContract(\"gas\", \"AllowGas\", from, cryptography.NullAddress().String(), big.NewInt(100000), big.NewInt(21000)).\n    CallInterop(\"Runtime.TransferTokens\", from, to, tokenSymbol, tokenAmount).\n    CallContract(\"gas\", \"SpendGas\", from).\n    EndScript()\n```\n\nAnd here we generate script to make a call which does not require transaction, for this we use `CallContract` method:\n\n```\naddress := \"put caller address here\" // Phantasma address, starting with capital 'P'\ntokenAmount := big.NewInt(1000000000) // Token amount in the form of big integer\n\nsb := scriptbuilder.BeginScript().\n    CallContract(\"gas\", \"AllowGas\", address, cryptography.NullAddress().String(), big.NewInt(100000), big.NewInt(21000)).\n    CallContract(\"stake\", \"Stake\", address, tokenAmount).\n    CallContract(\"gas\", \"SpendGas\", address)\nscript := sb.EndScript()\n```\n\n## Script Builder Extensions\n\nFor some widely used contract calls SDK has special extension methods which make code more compact. Here's the list of available extensions:\n\n```\nfunc (s ScriptBuilder) AllowGas(from, to string, gasPrice, gasLimit *big.Int)\n```\n\n```\nfunc (s ScriptBuilder) SpendGas(address string)\n```\n\n```\nfunc (s ScriptBuilder) MintTokens(symbol, from, to string, amount *big.Int)\n```\n\n```\nfunc (s ScriptBuilder) Stake(address string, amount *big.Int)\n```\n\n```\nfunc (s ScriptBuilder) Unstake(address string, amount *big.Int)\n```\n\n```\nfunc (s ScriptBuilder) TransferTokens(symbol, from, to string, amount *big.Int)\n```\n\n```\nfunc (s ScriptBuilder) TransferBalance(symbol, from, to string)\n```\n\n### Examples\n\nWe can rewrite examples from previous section using `AllowGas()` and `SpendGas()` extensions:\n\n```\nsb := scriptbuilder.BeginScript()\nscript := sb.AllowGas(from, cryptography.NullAddress().String(), big.NewInt(100000), big.NewInt(21000)).\n    CallInterop(\"Runtime.TransferTokens\", from, to, tokenSymbol, tokenAmount).\n    SpendGas(from).\n    EndScript()\n```\n```\nsb := scriptbuilder.BeginScript().\n    AllowGas(address, crypto.NullAddress().String(), big.NewInt(100000), big.NewInt(21000)).\n    CallContract(\"stake\", \"Stake\", address, tokenAmount).\n    SpendGas(address)\nscript := sb.EndScript()\n```\n\nWe can also rewrite main contract calls in these examples:\n\n```\nsb := scriptbuilder.BeginScript()\nscript := sb.AllowGas(from, cryptography.NullAddress().String(), big.NewInt(100000), big.NewInt(21000)).\n    TransferTokens(from, to, tokenSymbol, tokenAmount).\n    SpendGas(from).\n    EndScript()\n```\n```\nsb := scriptbuilder.BeginScript().\n    AllowGas(address, crypto.NullAddress().String(), big.NewInt(100000), big.NewInt(21000)).\n    Stake(address, tokenAmount).\n    SpendGas(address)\nscript := sb.EndScript()\n```\n\n## InvokeRawScript and decoding the result\n\nScripts which does not require transaction can be sent to the chain directly using `InvokeRawScript()` call.\n\nHere's an example of such call to get SoulMaster count from the chain:\n\n```\n// Build script\nsb := scriptbuilder.BeginScript().\n    CallContract(\"stake\", \"GetMasterCount\")\nscript := sb.EndScript()\n\n// Before sending script to the chain we need to encode it into Base16 encoding (HEX)\nencodedScript := hex.EncodeToString(script)\n\n// Make the call itself\nresult, err := client.InvokeRawScript(\"main\", encodedScript)\n\nif err != nil {\n    panic(\"Script invocation failed! Error: \" + err.Error())\n}\n\n// `DecodeResult()` decodes HEX-encoded byte array result, stored in `.Result` field, into `vm.VMObject` structure\n// `AsNumber()` returns value stored in `vm.VMObject` structure, in `.Data` field, as a *big.Int number (in our case value is stored in `vm.VMObject` as big integer serialized into byte array)\nfmt.Println(\"Current SoulMasters count: \", result.DecodeResult().AsNumber().String())\n```\n\n## Building and sending transaction\n\n### Building transaction\nTo build a transaction you will first need to build a script.\n\nNote, building a transaction is for transactional scripts only. Non transactional scripts should use the RPC function `InvokeRawScript()`.\n\n```\n// Build script\nsb := scriptbuilder.BeginScript()\nscript := sb.AllowGas(keyPair.Address().String(), cryptography.NullAddress().String(), big.NewInt(100000), big.NewInt(21000)).\n    TransferTokens(tokenSymbol, keyPair.Address().String(), to, tokenAmount).\n    SpendGas(keyPair.Address().String()).\n    EndScript()\n\n// Build transaction\nexpire := time.Now().UTC().Add(time.Second * time.Duration(30)).Unix()\ntx := blockchain.NewTransaction(netSelected, \"main\", script, uint32(expire), domain.SDKPayload)\n\n// Sign transaction\ntx.Sign(keyPair)\n\n// Before sending script to the chain we need to encode it into Base16 encoding (HEX)\ntxHex := hex.EncodeToString(tx.Bytes())\n```\n\n### Sending transaction\n\nHere we send transaction prepared in previous block of code and stored as HEX in `txHex` variable.\n\n```\ntxHash, err := client.SendRawTransaction(txHex)\nif err != nil {\n    panic(\"Broadcasting tx failed! Error: \" + err.Error())\n} else {\n    if util.ErrorDetect(txHash) {\n        panic(\"Broadcasting tx failed! Error: \" + txHash)\n    } else {\n        fmt.Println(\"Tx successfully broadcasted! Tx hash: \" + txHash)\n    }\n}\n```\n\n### Waiting for transaction execution result\n\nWe need to wait for transaction to be minted on the chain to get its status:\n\n```\nfor {\n    txResult, _ := client.GetTransaction(txHash)\n\n    if txResult.StateIsSuccess() {\n        fmt.Println(\"Transaction was successfully minted, tx hash: \" + fmt.Sprint(txResult.Hash))\n        break // Funds were transferred successfully\n    }\n    if txResult.StateIsFault() {\n        fmt.Println(\"Transaction failed, tx hash: \" + fmt.Sprint(txResult.Hash))\n        break // Funds were not transferred, transaction failed\n    }\n\n    time.Sleep(200 * time.Millisecond)\n}\n```\n\n## Staking SOUL token\n\nFollowing code shows how to stake SOUL token:\n\n```\n// Build script\nsb := scriptbuilder.BeginScript().\n    AllowGas(address, crypto.NullAddress().String(), big.NewInt(100000), big.NewInt(21000)).\n    Stake(address, tokenAmount).\n    SpendGas(address)\nscript := sb.EndScript()\n\n// Build transaction\nexpire := time.Now().UTC().Add(time.Second * time.Duration(30)).Unix()\ntx := chain.NewTransaction(netSelected, \"main\", script, uint32(expire), domain.SDKPayload)\n\n// Sign transaction\ntx.Sign(keyPair)\n\n// Before sending script to the chain we need to encode it into Base16 encoding (HEX)\ntxHex := hex.EncodeToString(tx.Bytes())\n\ntxHash, err := client.SendRawTransaction(txHex)\nif err != nil {\n    panic(\"Broadcasting tx failed! Error: \" + err.Error())\n} else {\n    if util.ErrorDetect(txHash) {\n        panic(\"Broadcasting tx failed! Error: \" + txHash)\n    } else {\n        fmt.Println(\"Tx successfully broadcasted! Tx hash: \" + txHash)\n    }\n}\n\nfor {\n    txResult, _ := client.GetTransaction(txHash)\n\n    if txResult.StateIsSuccess() {\n        fmt.Println(\"Transaction was successfully minted, tx hash: \" + fmt.Sprint(txResult.Hash))\n        break // Funds were transferred successfully\n    }\n    if txResult.StateIsFault() {\n        fmt.Println(\"Transaction failed, tx hash: \" + fmt.Sprint(txResult.Hash))\n        break // Funds were not transferred, transaction failed\n    }\n\n    time.Sleep(200 * time.Millisecond)\n}\n```\n\n## Scanning the blockchain for incoming transactions\n\nIn the following code we monitor the blockchain by checking all the new blocks minted on the blockchain and waiting for `TokenReceive` event for given address. This event for address means that address has received some tokens.\n\n```\nfunc onTransactionReceived(address, symbol, amount string) {\n    fmt.Printf(\"Address %s received %s %s\\n\", address, amount, symbol)\n}\n\nfunc waitForIncomingTransfers(address string) {\n    // Get current block height\n    height, _ := client.GetBlockHeight(\"main\")\n\n    for {\n        // Get block's data by its height\n        block, err := client.GetBlockByHeight(\"main\", height.String())\n        if err != nil {\n            panic(\"GetBlockByHeight call failed! Error: \" + err.Error())\n        }\n\n        // Iterate throough all transactions in the block\n        for _, tx := range block.Txs {\n            // Skip failed trasactions\n            if !tx.StateIsSuccess() {\n                continue\n            }\n\n            // Iterate throough all events in the transaction\n            for _, e := range tx.Events {\n\n                if e.Kind == event.TokenReceive.String() \u0026\u0026 e.Address == address {\n                    // We found TokenReceive event for given address\n\n                    // Decode event data into event.TokenEventData structure\n                    decoded, _ := hex.DecodeString(e.Data)\n                    data := io.Deserialize[*event.TokenEventData](decoded, \u0026event.TokenEventData{})\n\n                    // Apply decimals to the token amount\n                    t := getChainToken(data.Symbol)\n                    tokenAmount := util.ConvertDecimals(data.Value, int(t.Decimals))\n\n                    // Call our callback function\n                    onTransactionReceived(e.Address, data.Symbol, tokenAmount)\n                }\n            }\n        }\n\n        // Wait for next block to appear on the blockchain\n        for {\n            newHeight, _ := client.GetBlockHeight(\"main\")\n            if newHeight.Cmp(height) == 1 {\n                // New block was minted (at least 1 new block)\n                height = height.Add(height, big.NewInt(1))\n                break\n            }\n\n            // Wait 200 milliseconds before making next RPC call\n            time.Sleep(200 * time.Millisecond)\n        }\n    }\n}\n```\n\n## Examples\n\nThis repository has `examples` folder with some code which can be easily reused. Examples are grouped into a single console application.\n\nTo run this application switch to `examples` folder and run:\n\n```\ngo run .\n```\n\nor\n\n```\nsh run.sh\n```\n\nApplication entry point is `main()` function in `main.go` source file. Once launched it will display the following menu:\n\n![image](https://github.com/user-attachments/assets/42c7f5a5-41ea-4dfa-9a05-45028c563be6)\n\nWallet submenu:\n\n![image](https://github.com/user-attachments/assets/c6484554-3be8-415d-a8a0-1d885d0f9a37)\n\nChain stats submenu:\n\n![image](https://github.com/user-attachments/assets/7a1c0fec-9b92-4a8a-8e16-cd54f42b066f)\n\n\n# Contributing\n\nFeel free to contribute to this project after reading the\n[contributing guidelines](CONTRIBUTING.md).\n\nBefore starting to work on a certain topic, create an new issue first,\ndescribing the feature/topic you are going to implement.\n\n# Contact\n\n- Get in contact with us on the [Phantasma Discord](https://discord.gg/JzSnmFZCcD)\n\n# License\n\n- Open-source [MIT](LICENSE.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphantasma-io%2Fphantasma-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphantasma-io%2Fphantasma-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphantasma-io%2Fphantasma-go/lists"}