diff --git a/avail-go/examples/README.md b/avail-go/examples/README.md new file mode 100644 index 000000000..3738165c3 --- /dev/null +++ b/avail-go/examples/README.md @@ -0,0 +1,991 @@ +# Avail Go SDK documentation / examples + +## Running Examples in the Docs Directory + +To run the examples provided in the `/examples` directory, follow these steps: + +1. [Install](https://go.dev/doc/install) go globally if you haven't already: + +2. From the avail-go/examples, install all necessary dependencies: + +```bash +go mod tidy +``` + +3. Ensure that a config file is generated in the root of go-examples folder + +for example + +```json +{ + "seed": "bottom drive obey lake curtain smoke basket hold race lonely fit walk//Alice", + "api_url": "ws://127.0.0.1:9944", + "size": 1000, + "app_id": 0, + "dest": "5H3qehpRTFiB3XwyTzYU43SpG7e8jW87vFug95fxdew76Vyi", + "amount": 10 +} +``` + +4. Ensure you're running a local Avail node. You can do this with the following command from the root directory: + +```bash +cargo build --release +./target/release/avail-node --dev +``` + +You can also take the latest release from [Github](https://github.com/availproject/avail/releases) + +5. To run any example script from the examples/go-examples folder, use the following command format, replacing NAME_OF_THE_FILE with the actual file name you want to run: + +```bash +go run extrinsicFoldername/main.go --config config.json +``` + +For example, to run the staking_nominate.ts script: + +```bash +go run dataSubmit/main.go --config config.json +``` + +This will execute the chosen example script, showcasing how to interact with the Avail network using the avail-js-sdk. + +# Data Availability + +Runtime Component: DataAvailability\ +Runtime Index: 29\ +Interface Module Name: dataAvailability + +## Create Application Key + +Origin Level: Signed + +### Interface + +```go +func CreateApplicationKey(api *sdk.SubstrateAPI, seed string, data string, WaitForInclusion sdk.WaitFor) (types.Hash, types.Hash, error) +``` + +#### Parameters + +| parameter | type | optional | description | +| ---------------- | -------- | -------- | ------------------------------------------------------ | +| api | API | false | api for avail chain | +| WaitForInclusion | WaitFor | false | wait for block inclusion or finalization | +| Seed | Mnemonic | false | seed of the account that needs to sign the transaction | +| data | string | false | name of the application key | + +#### Return value + +On failure, a reason of failure is returned. On Success, ApplicationKeyCreated event, transaction hash and block hash is returned. + +### Minimal Example + +```go +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + + fmt.Println("Submitting data ...") + WaitFor := sdk.BlockInclusion + blockHash, txHash, err := tx.CreateApplicationKey(api, config.Seed, "my happyyy", WaitFor) + if err != nil { + fmt.Printf("cannot create application key:%v", err) + } + fmt.Printf("Application key created successfully with block hash: %v\n and ext hash:%v\n", blockHash.Hex(), txHash.Hex()) + sdk.EventParser(api, blockHash, "ApplicationKeyCreated") +} +``` + +## Submit Data + +Origin Level: Signed + +### Interface + +```go +func SubmitData(api *sdk.SubstrateAPI, seed string, AppID int, data string, WaitForInclusion sdk.WaitFor) (types.Hash, types.Hash, error) +``` + +#### Parameters + +| parameter | type | optional | description | +| ---------------- | ------------- | -------- | ------------------------------------------------------ | +| api | API | false | api for avail chain | +| WaitForInclusion | WaitFor | false | wait for block inclusion or finalization | +| Seed | Mnemonic | false | seed of the account that needs to sign the transaction | +| data | SignerOptions | true | data to be submitted | +| AppID | SignerOptions | true | AppID in which the transaction needs to be signed | + +#### Return value + +On failure, a reason of failure is returned. On Success, DataSubmitted event, transaction data, transaction hash and block hash is returned. + +### Minimal Example + +```go +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + + appID := 0 + + // if app id is greater than 0 then it must be created before submitting data + if config.AppID != 0 { + appID = config.AppID + } + fmt.Println("Submitting data ...") + WaitFor := sdk.BlockInclusion + + BlockHash, txHash, err := tx.SubmitData(api, config.Seed, appID, "my happy data", WaitFor) + if err != nil { + fmt.Printf("cannot submit data:%v", err) + } + fmt.Printf("Data submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) +} +``` + +## Submit Block Length Proposal + +Origin Level: Root + +### Interface + +```go +func SubmitBlockLength(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, rows uint32, cols uint32) (types.Hash, types.Hash, error) +``` + +#### Parameters + +| parameter | type | optional | description | +| ---------------- | -------- | -------- | ------------------------------------------------------ | +| api | API | false | api for avail chain | +| rows | number | false | number of rows in block | +| cols | number | false | number of cols in block | +| WaitForInclusion | WaitFor | false | wait for block inclusion or finalization | +| Seed | Mnemonic | false | seed of the account that needs to sign the transaction | + +#### Return value + +On failure, a reason of failure is returned. On Success, transaction hash and block hash is returned. + +### Minimal Example + +```go +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + + fmt.Println("Submitting data ...") + WaitFor := sdk.BlockInclusion + rows := uint32(128) + cols := uint32(128) + blockHash, txHash, err := tx.SubmitBlockLength(api, config.Seed, WaitFor, rows, cols) + if err != nil { + fmt.Printf("cannot submit block length:%v", err) + } + fmt.Printf("Block Length updated successfully with block hash: %v\n and ext hash:%v\n", blockHash.Hex(), txHash.Hex()) +} +``` + +## Set Application Key + +Origin Level: Root + +### Interface + +```go +func SetApplicationKey(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, oldKey string, newKey string) (types.Hash, types.Hash, error) +``` + +#### Parameters + +| parameter | type | optional | description | +| ---------------- | -------- | -------- | ------------------------------------------------------ | +| api | API | false | api for avail chain | +| oldKey | string | false | application key to be replaced | +| newKey | string | false | application key that will replace the old one | +| WaitForInclusion | WaitFor | false | wait for block inclusion or finalization | +| Seed | Mnemonic | false | seed of the account that needs to sign the transaction | + +#### Return value + +On failure, a reason of failure is returned. On Success, ApplicationKeySet event, transaction hash and block hash is returned. + +### Minimal Example + +```go +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + fmt.Println("Submitting data ...") + WaitFor := sdk.BlockInclusion + newKey := "newKey" + oldKey := "oldKey" + blockHash, txHash, err := tx.SetApplicationKey(api, config.Seed, WaitFor, oldKey, newKey) + if err != nil { + fmt.Printf("cannot set key:%v", err) + } + fmt.Printf("Application Key updated successfully with block hash: %v\n and ext hash:%v\n", blockHash.Hex(), txHash.Hex()) + sdk.EventParser(api, blockHash, "ApplicationKeySet") +} +``` + +## Set Submit Data Fee Modifer + +Origin Level: Root + +### Interface + +```go +func SetSubmitDataFeeModifier(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, modifier sdk.DispatchFeeModifier) (types.Hash, types.Hash, error) +``` + +#### Parameters + +| parameter | type | optional | description | +| ---------------- | ------------------- | -------- | ------------------------------------------------------ | +| api | API | false | api for avail chain | +| modifier | DispatchFeeModifier | false | new fee modifier values | +| WaitForInclusion | WaitFor | false | wait for block inclusion or finalization | +| Seed | Mnemonic | false | seed of the account that needs to sign the transaction | + +#### Return value + +On failure, a reason of failure is returned. On Success, transaction hash and block hash is returned. + +### Minimal Example + +```go +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + "math/big" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + tenPow18 := new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil) + + weightMaximumFee := sdk.NewU128(tenPow18) + weightFeeDivider := sdk.NewU32(20) + weightFeeMultiplier := sdk.NewU32(1) + + // Create the DispatchFeeModifier + modifier := sdk.DispatchFeeModifier{ + WeightMaximumFee: weightMaximumFee, + WeightFeeDivider: weightFeeDivider, + WeightFeeMultiplier: weightFeeMultiplier, + } + fmt.Println("Submitting data ...") + WaitFor := sdk.BlockInclusion + // submit data + blockHash, txHash, err := tx.SetSubmitDataFeeModifier(api, config.Seed, WaitFor, modifier) + if err != nil { + fmt.Printf("cannot update DA fee:%v", err) + } + fmt.Printf("Data Fee modified successfully with block hash: %v\n and ext hash:%v\n", blockHash.Hex(), txHash.Hex()) +} +``` + +## Type Definitions + +```go +type WaitFor int + +const ( + BlockInclusion WaitFor = iota + 1 + BlockFinalization +) +``` + +```go +type DispatchFeeModifier struct { + WeightMaximumFee types.U128 + WeightFeeDivider types.U32 + WeightFeeMultiplier types.U32 +} +``` + +# Balances + +Runtime Component: Balances\ +Runtime Index: 6\ +Interface Module Name: balances + +## Transfer Keep Alive + +Origin Level: Signed + +### Interface + +```go +func TransferKeepAlive(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, dest string, amount types.UCompact) (types.Hash, types.Hash, error) +``` + +#### Parameters + +| parameter | type | optional | description | +| ---------------- | -------- | -------- | ------------------------------------------------------ | +| api | API | false | api for avail chain | +| dest | string | false | account that will receive funds | +| amount | Ucompact | false | amount that is send. 10^18 is equal to 1 AVL | +| WaitForInclusion | WaitFor | false | wait for block inclusion or finalization | +| Seed | Mnemonic | false | seed of the account that needs to sign the transaction | + +#### Return value + +On failure, a reason of failure is returned. On Success, TransferEvent event, transaction hash and block hash is returned. + +### Minimal Example + +```go +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + "avail-go-sdk/src/sdk/types" + "math" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + + amount := uint64(math.Pow(10, 18)) * 10 // send amount 10 AVAIL + dest := "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" + bondAmountUCompact := types.NewUCompactFromUInt(amount) + BlockHash, txHash, err := tx.TransferKeepAlive(api, config.Seed, WaitFor, dest, bondAmountUCompact) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) + sdk.EventParser(api, BlockHash, "BalanceTransfer") +} +``` + +## Transfer Allow Death + +Origin Level: Signed + +### Interface + +```go +func TransferAllowDeath(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, dest string, amount types.UCompact) (types.Hash, types.Hash, error) +``` + +#### Parameters + +| parameter | type | optional | description | +| ---------------- | -------- | -------- | ------------------------------------------------------ | +| api | API | false | api for avail chain | +| dest | string | false | account that will receive funds | +| amount | Ucompact | false | amount that is send. 10^18 is equal to 1 AVL | +| WaitForInclusion | WaitFor | false | wait for block inclusion or finalization | +| Seed | Mnemonic | false | seed of the account that needs to sign the transaction | + +#### Return value + +On failure, a reason of failure is returned. On Success, TransferEvent event, KilledAccount (optionally) event, transaction hash and block +hash is returned. + +### Minimal Example + +```go +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + "avail-go-sdk/src/sdk/types" + "math" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + + amount := uint64(math.Pow(10, 18)) * 10 // send amount 10 AVAIL + dest := "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" + bondAmountUCompact := types.NewUCompactFromUInt(amount) + BlockHash, txHash, err := tx.TransferAllowDeath(api, config.Seed, WaitFor, dest, bondAmountUCompact) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) + sdk.EventParser(api, BlockHash, "BalanceTransfer") +} +``` + +## Transfer All + +Origin Level: Signed + +### Interface + +```go +func TransferAll(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, dest string) (types.Hash, types.Hash, error) +``` + +#### Parameters + +| parameter | type | optional | description | +| ---------------- | -------- | -------- | ------------------------------------------------------ | +| api | API | false | api for avail chain | +| dest | string | false | account that will receive funds | +| WaitForInclusion | WaitFor | false | wait for block inclusion or finalization | +| Seed | Mnemonic | false | seed of the account that needs to sign the transaction | + +#### Return value + +On failure, a reason of failure is returned. On Success transaction hash and block hash is returned. + +### Minimal Example + +```go +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + + dest := "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY" + BlockHash, txHash, err := tx.TransferAll(api, config.Seed, WaitFor, dest) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) +} +``` + +# Staking + +Runtime Component: Staking\ +Runtime Index: 10\ +Interface Module Name: staking + +### Type Definitions + +```go +type Payee uint8 + +const ( +Staked WaitFor = iota +Stash +Controller +Account +None +) +``` + +## Bond + +Origin Level: Signed + +### Interface + +```go +func Bond(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, amount types.UCompact, Payee sdk.Payee) (types.Hash, types.Hash, error) +``` + +#### Parameters + +| parameter | type | optional | description | +| ---------------- | -------- | -------- | ------------------------------------------------------- | +| api | API | false | api for avail chain | +| amount | Ucompact | false | amount that is bond. | +| payee | Payee | false | Can be: "Staked", "Stash", "None" or an account address | +| WaitForInclusion | WaitFor | false | wait for block inclusion or finalization | +| Seed | Mnemonic | false | seed of the account that needs to sign the transaction | + +#### Return value + +On failure, a reason of failure is returned. On Success, Bonded event, transaction hash and block hash is returned. + +### Minimal Example + +```go +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + Payee := sdk.Staked + + bondAmount := int64(1000) + + BlockHash, txHash, err := tx.Bond(api, config.Seed, WaitFor, bondAmount, sdk.Payee(Payee)) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) + sdk.EventParser(api, BlockHash, "Bond") +} +``` + +## Bond Extra + +Origin Level: Signed + +### Interface + +```go +func BondExtra(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, amount types.UCompact) (types.Hash, types.Hash, error) +``` + +#### Parameters + +| parameter | type | optional | description | +| ---------------- | ----------- | -------- | ------------------------------------------------------- | +| api | API | false | api for avail chain | +| amount | Ucompact | false | additional amount that is bond. 10^18 is equal to 1 AVL | +| WaitForInclusion | WaitFor | false | wait for block inclusion or finalization | +| account | KeyringPair | false | account that will send and sign the transaction | +| Seed | Mnemonic | false | seed of the account that needs to sign the transaction | + +#### Return value + +On failure, a reason of failure is returned. On Success, transaction hash and block hash is returned. + +### Minimal Example + +```go +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + + bondAmount := int64(1000) + + BlockHash, txHash, err := tx.BondExtra(api, config.Seed, WaitFor, bondAmount) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) +} +``` + +## Chill + +Origin Level: Signed + +### Interface + +```go +func Chill(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor) (types.Hash, types.Hash, error) +``` + +#### Parameters + +| parameter | type | optional | description | +| ---------------- | -------- | -------- | ------------------------------------------------------ | +| WaitForInclusion | WaitFor | false | wait for block inclusion or finalization | +| api | API | false | api for avail chain | +| Seed | Mnemonic | false | seed of the account that needs to sign the transaction | + +#### Return value + +On failure, a reason of failure is returned. On Success, transaction hash and block hash is returned. + +### Minimal Example + +```go +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + + BlockHash, txHash, err := tx.Chill(api, config.Seed, WaitFor) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) +} +``` + +## Chill Other + +Origin Level: Signed + +### Interface + +```go +func ChillOther(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, stash string) (types.Hash, types.Hash, error) +``` + +#### Parameters + +| parameter | type | optional | description | +| ---------------- | -------- | -------- | ------------------------------------------------------ | +| stash | string | false | Address that needs to be chilled | +| WaitForInclusion | WaitFor | false | wait for block inclusion or finalization | +| api | API | false | api for avail chain | +| Seed | Mnemonic | false | seed of the account that needs to sign the transaction | + +#### Return value + +On failure, a reason of failure is returned. On Success, transaction hash and block hash is returned. + +### Minimal Example + +```go +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + + stash := "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY" + BlockHash, txHash, err := tx.ChillOther(api, config.Seed, WaitFor, stash) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) +} +``` + +## Nominate + +Origin Level: Signed + +### Interface + +```go +func Nominate(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, stash []string) (types.Hash, types.Hash, error) +``` + +#### Parameters + +| parameter | type | optional | description | +| ---------------- | -------- | -------- | ------------------------------------------------------ | +| stash | string[] | false | list od addresses to nominate | +| WaitForInclusion | WaitFor | false | wait for block inclusion or finalization | +| api | API | false | api for avail chain | +| Seed | Mnemonic | false | seed of the account that needs to sign the transaction | + +#### Return value + +On failure, a reason of failure is returned. On Success, transaction hash and block hash is returned. + +### Minimal Example + +```go +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + + stash := []string{"5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"} + BlockHash, txHash, err := tx.Nominate(api, config.Seed, WaitFor, stash) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) +} +``` + +## Unbond + +Origin Level: Signed + +### Interface + +```go +func Unbond(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, amount types.UCompact) (types.Hash, types.Hash, error) +``` + +#### Parameters + +| parameter | type | optional | description | +| ---------------- | -------- | -------- | ------------------------------------------------------ | +| amount | Ucompact | false | amount of tokens to unbond | +| WaitForInclusion | WaitFor | false | wait for block inclusion or finalization | +| api | API | false | api for avail chain | +| Seed | Mnemonic | false | seed of the account that needs to sign the transaction | + +#### Return value + +On failure, a reason of failure is returned. On Success, Unbonded event, transaction hash and block hash is returned. + +### Minimal Example + +```go +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + "avail-go-sdk/src/sdk/types" + "math/big" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + + bondAmount := new(big.Int) + bondAmount.SetString("100000000000000000000", 10) + + // Convert big.Int to types.UCompact + bondAmountUCompact := types.NewUCompact(bondAmount) + BlockHash, txHash, err := tx.Unbond(api, config.Seed, WaitFor, bondAmountUCompact) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) +} +``` + +## Validate + +Origin Level: Signed + +### Interface + +```go +func Validate(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, commissionNum int) (types.Hash, types.Hash, error) +``` + +#### Parameters + +| parameter | type | optional | description | +| ---------------- | -------- | -------- | ------------------------------------------------------ | +| commission | number | false | how much validator charge nominators in 0 - 100 range | +| WaitForInclusion | WaitFor | false | wait for block inclusion or finalization | +| api | API | false | api for avail chain | +| Seed | Mnemonic | false | seed of the account that needs to sign the transaction | + +#### Return value + +On failure, a reason of failure is returned. On Success, transaction hash and block hash is returned. + +### Minimal Example + +```js +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + commission := 5 + BlockHash, txHash, err := tx.Validate(api, config.Seed, WaitFor, commission) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) +} +``` diff --git a/avail-go/examples/balance_allow_death/main.go b/avail-go/examples/balance_allow_death/main.go new file mode 100644 index 000000000..6441264d3 --- /dev/null +++ b/avail-go/examples/balance_allow_death/main.go @@ -0,0 +1,33 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + "avail-go-sdk/src/sdk/types" + "math" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + + amount := uint64(math.Pow(10, 18)) * 10 // send amount 10 AVAIL + dest := "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" + bondAmountUCompact := types.NewUCompactFromUInt(amount) + BlockHash, txHash, err := tx.TransferAllowDeath(api, config.Seed, WaitFor, dest, bondAmountUCompact) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) + sdk.EventParser(api, BlockHash, "BalanceTransfer") +} diff --git a/avail-go/examples/balance_transfer_all/main.go b/avail-go/examples/balance_transfer_all/main.go new file mode 100644 index 000000000..4d74d97ac --- /dev/null +++ b/avail-go/examples/balance_transfer_all/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + + dest := "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY" + BlockHash, txHash, err := tx.TransferAll(api, config.Seed, WaitFor, dest) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) +} diff --git a/avail-go/examples/balances_keep_alive/main.go b/avail-go/examples/balances_keep_alive/main.go new file mode 100644 index 000000000..561649178 --- /dev/null +++ b/avail-go/examples/balances_keep_alive/main.go @@ -0,0 +1,33 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + "avail-go-sdk/src/sdk/types" + "math" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + + amount := uint64(math.Pow(10, 18)) * 10 // send amount 10 AVAIL + dest := "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" + bondAmountUCompact := types.NewUCompactFromUInt(amount) + BlockHash, txHash, err := tx.TransferKeepAlive(api, config.Seed, WaitFor, dest, bondAmountUCompact) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) + sdk.EventParser(api, BlockHash, "BalanceTransfer") +} diff --git a/examples/go/config.json b/avail-go/examples/config.json similarity index 100% rename from examples/go/config.json rename to avail-go/examples/config.json diff --git a/examples/go/connect/main.go b/avail-go/examples/connect/main.go similarity index 52% rename from examples/go/connect/main.go rename to avail-go/examples/connect/main.go index 418da7c56..ccfb67cfe 100644 --- a/examples/go/connect/main.go +++ b/avail-go/examples/connect/main.go @@ -1,36 +1,20 @@ package main import ( - "avail-gsrpc-examples/internal/config" - "flag" + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" "fmt" - "log" - "os" - - gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" ) // The following example shows how to connect to a node and display some basic information. func main() { - var configJSON string - var config config.Config - - flag.StringVar(&configJSON, "config", "", "config json file") - flag.Parse() - - if configJSON == "" { - log.Println("No config file provided. Exiting...") - os.Exit(0) - } - - err := config.GetConfig(configJSON) + config, err := config.LoadConfig() if err != nil { - panic(fmt.Sprintf("cannot get config:%v", err)) + fmt.Printf("cannot load config:%v", err) } - - api, err := gsrpc.NewSubstrateAPI(config.ApiURL) + api, err := sdk.NewSDK(config.ApiURL) if err != nil { - panic(fmt.Sprintf("cannot create api client:%v", err)) + fmt.Printf("cannot create api:%v", err) } chain, err := api.RPC.System.Chain() if err != nil { diff --git a/avail-go/examples/da_create_application_key/main.go b/avail-go/examples/da_create_application_key/main.go new file mode 100644 index 000000000..409d6ab6b --- /dev/null +++ b/avail-go/examples/da_create_application_key/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + + fmt.Println("Submitting data ...") + WaitFor := sdk.BlockInclusion + blockHash, txHash, err := tx.CreateApplicationKey(api, config.Seed, "my happyyy", WaitFor) + if err != nil { + fmt.Printf("cannot create application key:%v", err) + } + fmt.Printf("Application key created successfully with block hash: %v\n and ext hash:%v\n", blockHash.Hex(), txHash.Hex()) + sdk.EventParser(api, blockHash, "ApplicationKeyCreated") +} diff --git a/avail-go/examples/da_data_submit/main.go b/avail-go/examples/da_data_submit/main.go new file mode 100644 index 000000000..ddce46a25 --- /dev/null +++ b/avail-go/examples/da_data_submit/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + + appID := 0 + + // if app id is greater than 0 then it must be created before submitting data + if config.AppID != 0 { + appID = config.AppID + } + fmt.Println("Submitting data ...") + WaitFor := sdk.BlockInclusion + + // submit data + BlockHash, txHash, err := tx.SubmitData(api, config.Seed, appID, "my happy data", WaitFor) + if err != nil { + fmt.Printf("cannot submit data:%v", err) + } + fmt.Printf("Data submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) +} diff --git a/avail-go/examples/da_data_submit_watch/main.go b/avail-go/examples/da_data_submit_watch/main.go new file mode 100644 index 000000000..7bba15292 --- /dev/null +++ b/avail-go/examples/da_data_submit_watch/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + + appID := 0 + + // if app id is greater than 0 then it must be created before submitting data + if config.AppID != 0 { + appID = config.AppID + } + fmt.Println("Submitting data ...") + WaitFor := sdk.BlockFinalization + // submit data + blockHash, txHash, err := tx.SubmitData(api, config.Seed, appID, "my happy data", WaitFor) + if err != nil { + fmt.Printf("cannot submit data:%v", err) + } + fmt.Printf("Data submitted successfully with block hash: %v\n and ext hash:%v\n", blockHash.Hex(), txHash.Hex()) + sdk.EventParser(api, blockHash, "DataSubmitted") +} diff --git a/avail-go/examples/da_set_application_key/main.go b/avail-go/examples/da_set_application_key/main.go new file mode 100644 index 000000000..2d5711e35 --- /dev/null +++ b/avail-go/examples/da_set_application_key/main.go @@ -0,0 +1,30 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + fmt.Println("Submitting data ...") + WaitFor := sdk.BlockInclusion + newKey := "newKey" + oldKey := "oldKey" + blockHash, txHash, err := tx.SetApplicationKey(api, config.Seed, WaitFor, oldKey, newKey) + if err != nil { + fmt.Printf("cannot set key:%v", err) + } + fmt.Printf("Application Key updated successfully with block hash: %v\n and ext hash:%v\n", blockHash.Hex(), txHash.Hex()) + sdk.EventParser(api, blockHash, "ApplicationKeySet") +} diff --git a/avail-go/examples/da_submit_block_length/main.go b/avail-go/examples/da_submit_block_length/main.go new file mode 100644 index 000000000..577df62e0 --- /dev/null +++ b/avail-go/examples/da_submit_block_length/main.go @@ -0,0 +1,30 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + + fmt.Println("Submitting data ...") + WaitFor := sdk.BlockInclusion + rows := uint32(128) + cols := uint32(128) + blockHash, txHash, err := tx.SubmitBlockLength(api, config.Seed, WaitFor, rows, cols) + if err != nil { + fmt.Printf("cannot submit block length:%v", err) + } + fmt.Printf("Block Length updated successfully with block hash: %v\n and ext hash:%v\n", blockHash.Hex(), txHash.Hex()) +} diff --git a/avail-go/examples/da_submit_data_fee_modifier/main.go b/avail-go/examples/da_submit_data_fee_modifier/main.go new file mode 100644 index 000000000..6d1e9bafa --- /dev/null +++ b/avail-go/examples/da_submit_data_fee_modifier/main.go @@ -0,0 +1,41 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + "math/big" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + tenPow18 := new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil) + + weightMaximumFee := sdk.NewU128(tenPow18) + weightFeeDivider := sdk.NewU32(20) + weightFeeMultiplier := sdk.NewU32(1) + + // Create the DispatchFeeModifier + modifier := sdk.DispatchFeeModifier{ + WeightMaximumFee: weightMaximumFee, + WeightFeeDivider: weightFeeDivider, + WeightFeeMultiplier: weightFeeMultiplier, + } + fmt.Println("Submitting data ...") + WaitFor := sdk.BlockInclusion + blockHash, txHash, err := tx.SetSubmitDataFeeModifier(api, config.Seed, WaitFor, modifier) + if err != nil { + fmt.Printf("cannot update DA fee:%v", err) + } + fmt.Printf("Data Fee modified successfully with block hash: %v\n and ext hash:%v\n", blockHash.Hex(), txHash.Hex()) + +} diff --git a/avail-go/examples/existential/main.go b/avail-go/examples/existential/main.go new file mode 100644 index 000000000..bcba9c889 --- /dev/null +++ b/avail-go/examples/existential/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + sdk.ExistentialDeposit(api) +} diff --git a/avail-go/examples/go.mod b/avail-go/examples/go.mod new file mode 100644 index 000000000..536d7dba4 --- /dev/null +++ b/avail-go/examples/go.mod @@ -0,0 +1,34 @@ +module avail-go-sdk-examples + +go 1.21.0 + +require avail-go-sdk v0.0.0 + +require ( + github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect + github.com/centrifuge/go-substrate-rpc-client/v4 v4.2.1 // indirect + github.com/cosmos/go-bip39 v1.0.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/deckarep/golang-set v1.8.0 // indirect + github.com/decred/base58 v1.0.4 // indirect + github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect + github.com/ethereum/go-ethereum v1.10.20 // indirect + github.com/go-stack/stack v1.8.1 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/gtank/merlin v0.1.1 // indirect + github.com/gtank/ristretto255 v0.1.2 // indirect + github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b // indirect + github.com/pierrec/xxHash v0.1.5 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rs/cors v1.8.2 // indirect + github.com/stretchr/objx v0.1.1 // indirect + github.com/stretchr/testify v1.7.2 // indirect + github.com/vedhavyas/go-subkey v1.0.4 // indirect + github.com/vedhavyas/go-subkey/v2 v2.0.0 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/sys v0.19.0 // indirect + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace avail-go-sdk => ../../avail-go diff --git a/examples/go/go.sum b/avail-go/examples/go.sum similarity index 83% rename from examples/go/go.sum rename to avail-go/examples/go.sum index 2f733f075..6f9ebbf6a 100644 --- a/examples/go/go.sum +++ b/avail-go/examples/go.sum @@ -1,11 +1,14 @@ github.com/ChainSafe/go-schnorrkel v1.0.0 h1:3aDA67lAykLaG1y3AOjs88dMxC88PgUuHRrLeDnvGIM= github.com/ChainSafe/go-schnorrkel v1.0.0/go.mod h1:dpzHYVxLZcp8pjlV+O+UR8K0Hp/z7vcchBSbMBEhCw4= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= -github.com/availproject/go-substrate-rpc-client/v4 v4.1.0-avail-2.1.5-rc1 h1:mIYkc6eyi3iANW/dC+g7P49OfL54xnM61bq9B3a5pGw= -github.com/availproject/go-substrate-rpc-client/v4 v4.1.0-avail-2.1.5-rc1/go.mod h1:MeGquF7RkixfmXKXqwFF0a1iPLQiJGVlHjSLnJD4SD4= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= +github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= +github.com/centrifuge/go-substrate-rpc-client/v4 v4.2.1 h1:io49TJ8IOIlzipioJc9pJlrjgdJvqktpUWYxVY5AUjE= +github.com/centrifuge/go-substrate-rpc-client/v4 v4.2.1/go.mod h1:k61SBXqYmnZO4frAJyH3iuqjolYrYsq79r8EstmklDY= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= @@ -19,12 +22,15 @@ github.com/decred/base58 v1.0.4/go.mod h1:jJswKPEdvpFpvf7dsDvFZyLT22xZ9lWqEByX38 github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/ethereum/go-ethereum v1.10.20 h1:75IW830ClSS40yrQC1ZCMZCt5I+zU16oqId2SiQwdQ4= github.com/ethereum/go-ethereum v1.10.20/go.mod h1:LWUN82TCHGpxB3En5HVmLLzPD7YSrEUFmFfN1nKkVN0= github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64= +github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= @@ -42,14 +48,18 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= +github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= +github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/vedhavyas/go-subkey v1.0.4 h1:QwjBZx4w7qXC2lmqol2jJfhaNXPI9BsgLZiMiCwqGDU= github.com/vedhavyas/go-subkey v1.0.4/go.mod h1:aOIil/KS9hJlnr9ZSQKSoXdu/MbnkCxG4x9IOlLsMtI= github.com/vedhavyas/go-subkey/v2 v2.0.0 h1:LemDIsrVtRSOkp0FA8HxP6ynfKjeOj3BY2U9UNfeDMA= diff --git a/examples/go/listenNewBlocks/main.go b/avail-go/examples/listenNewBlocks/main.go similarity index 50% rename from examples/go/listenNewBlocks/main.go rename to avail-go/examples/listenNewBlocks/main.go index c48753566..264d0b956 100644 --- a/examples/go/listenNewBlocks/main.go +++ b/avail-go/examples/listenNewBlocks/main.go @@ -1,35 +1,21 @@ package main import ( - "avail-gsrpc-examples/internal/config" - "flag" "fmt" - "log" - "os" - gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" ) // The following example shows how to connect to a node and listen for a new blocks func main() { - var configJSON string - var config config.Config - flag.StringVar(&configJSON, "config", "", "config json file") - flag.Parse() - - if configJSON == "" { - log.Println("No config file provided. Exiting...") - os.Exit(0) - } - - err := config.GetConfig(configJSON) + config, err := config.LoadConfig() if err != nil { - panic(fmt.Sprintf("cannot get config:%v", err)) + fmt.Printf("cannot load config:%v", err) } - - api, err := gsrpc.NewSubstrateAPI(config.ApiURL) + api, err := sdk.NewSDK(config.ApiURL) if err != nil { - panic(fmt.Sprintf("cannot create api client:%v", err)) + fmt.Printf("cannot create api:%v", err) } subscription, err := api.RPC.Chain.SubscribeNewHeads() diff --git a/avail-go/examples/properties/main.go b/avail-go/examples/properties/main.go new file mode 100644 index 000000000..399d0a56d --- /dev/null +++ b/avail-go/examples/properties/main.go @@ -0,0 +1,30 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/types" + "fmt" +) + +func main() { + + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + + var properties types.ChainProperties + err = api.Client.Call(&properties, "system_properties", nil) + + if err != nil { + panic(fmt.Sprintf("cannot get properties:%v", err)) + } + + fmt.Printf("\nChain properties:TokenSymbol:%#v\nTokenDecimals:%d\nSS58Format:%d\n", properties.TokenSymbol, properties.TokenDecimals, properties.SS58Format) + +} diff --git a/avail-go/examples/rpc/author_rotate/main.go b/avail-go/examples/rpc/author_rotate/main.go new file mode 100644 index 000000000..7de0c6bc6 --- /dev/null +++ b/avail-go/examples/rpc/author_rotate/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/call" + "fmt" + "log" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + if api == nil || api.Client == nil { + log.Fatal("API client is not properly initialized") + } + resp, err := call.Author_Rotate(api) + if err != nil { + fmt.Printf("cannot author rotate:%v", err) + } + fmt.Printf("New session keys: 0x%x\n", resp) +} diff --git a/avail-go/examples/rpc/chain_finalizedHead/main.go b/avail-go/examples/rpc/chain_finalizedHead/main.go new file mode 100644 index 000000000..751cd1935 --- /dev/null +++ b/avail-go/examples/rpc/chain_finalizedHead/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/rpc" + "avail-go-sdk/src/sdk" + "fmt" + "log" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + if api == nil || api.Client == nil { + log.Fatal("API client is not properly initialized") + } + resp, err := rpc.GetFinalizedHead(api.Client) + if err != nil { + fmt.Printf("cannot call finalized header RPC:%v", err) + } + fmt.Println(resp.Hex()) +} diff --git a/avail-go/examples/rpc/chain_getBlock/main.go b/avail-go/examples/rpc/chain_getBlock/main.go new file mode 100644 index 000000000..fda587803 --- /dev/null +++ b/avail-go/examples/rpc/chain_getBlock/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/rpc" + "avail-go-sdk/src/sdk" + "fmt" + "log" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + if api == nil || api.Client == nil { + log.Fatal("API client is not properly initialized") + } + resp, err := rpc.GetAvailBlockLatest(api.Client) + if err != nil { + fmt.Printf("cannot author rotate:%v", err) + } + fmt.Println(resp) +} diff --git a/avail-go/examples/rpc/chain_getBlockHash/main.go b/avail-go/examples/rpc/chain_getBlockHash/main.go new file mode 100644 index 000000000..8175d46a9 --- /dev/null +++ b/avail-go/examples/rpc/chain_getBlockHash/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/rpc" + "avail-go-sdk/src/sdk" + "fmt" + "log" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + if api == nil || api.Client == nil { + log.Fatal("API client is not properly initialized") + } + resp, err := rpc.GetBlockHashLatest(api.Client) + if err != nil { + fmt.Printf("cannot call latest block hash RPC:%v", err) + } + fmt.Println(resp.Hex()) +} diff --git a/avail-go/examples/rpc/chain_getHeader/main.go b/avail-go/examples/rpc/chain_getHeader/main.go new file mode 100644 index 000000000..ad61997fa --- /dev/null +++ b/avail-go/examples/rpc/chain_getHeader/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/rpc" + "avail-go-sdk/src/sdk" + "fmt" + "log" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + if api == nil || api.Client == nil { + log.Fatal("API client is not properly initialized") + } + resp, err := rpc.GetHeaderLatest(api.Client) + if err != nil { + fmt.Printf("cannot call latest header RPC:%v", err) + } + fmt.Println(resp) +} diff --git a/avail-go/examples/rpc/kate_queryProof/main.go b/avail-go/examples/rpc/kate_queryProof/main.go new file mode 100644 index 000000000..4a4acea22 --- /dev/null +++ b/avail-go/examples/rpc/kate_queryProof/main.go @@ -0,0 +1,53 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/rpc" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/call" + "avail-go-sdk/src/sdk/tx" + "encoding/json" + "fmt" + "log" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + if api == nil || api.Client == nil { + log.Fatal("API client is not properly initialized") + } + + appID := 0 + + // if app id is greater than 0 then it must be created before submitting data + if config.AppID != 0 { + appID = config.AppID + } + WaitFor := sdk.BlockFinalization + + blockHash, txHash, err := tx.SubmitData(api, config.Seed, appID, "my happy data", WaitFor) + if err != nil { + fmt.Printf("cannot submit data:%v", err) + } + fmt.Printf("Data submitted successfully with block hash: %v\n and ext hash:%v\n", blockHash.Hex(), txHash.Hex()) + testCell := []rpc.Cell{rpc.NewCell(0, 0), rpc.NewCell(0, 1)} + result, err := call.Query_proof(api, testCell, blockHash) + if err != nil { + fmt.Printf("cannot query proof:%v", err) + } + + resultJSON, err := json.Marshal(result) + if err != nil { + fmt.Println("Error marshalling result to JSON:", err) + return + } + + fmt.Println(string(resultJSON)) +} diff --git a/avail-go/examples/rpc/kate_queryProofData/main.go b/avail-go/examples/rpc/kate_queryProofData/main.go new file mode 100644 index 000000000..f5376df7e --- /dev/null +++ b/avail-go/examples/rpc/kate_queryProofData/main.go @@ -0,0 +1,74 @@ +package main + +import ( + "fmt" + "log" + + "avail-go-sdk/src/config" + "avail-go-sdk/src/rpc" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/call" + "avail-go-sdk/src/sdk/tx" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + if api == nil || api.Client == nil { + log.Fatal("API client is not properly initialized") + } + + appID := 0 + + // if app id is greater than 0 then it must be created before submitting data + if config.AppID != 0 { + appID = config.AppID + } + WaitFor := sdk.BlockFinalization + + blockHash, _, err := tx.SubmitData(api, config.Seed, appID, "my happy data", WaitFor) + if err != nil { + fmt.Printf("cannot submit data:%v", err) + } + fmt.Printf("Transaction included in finalized block: %v\n", blockHash.Hex()) + + transactionIndex := sdk.NewU32(1) + + // query proof + response, err := call.Query_dataproof(api, transactionIndex, blockHash) + if err != nil { + fmt.Printf("cannot query proof:%v", err) + } + fmt.Printf("DataRoot:%v\n", response.DataProof.Roots.DataRoot.Hex()) + fmt.Printf("BlobRoot:%v\n", response.DataProof.Roots.BlobRoot.Hex()) + fmt.Printf("BridgeRoot:%v\n", response.DataProof.Roots.BridgeRoot.Hex()) + // print array of proof + fmt.Printf("Proof:\n") + for _, p := range response.DataProof.Proof { + fmt.Printf("%v\n", p.Hex()) + } + + fmt.Printf("Number of leaves: %v\n", response.DataProof.NumberOfLeaves) + fmt.Printf("Leaf index: %v\n", response.DataProof.LeafIndex) + fmt.Printf("Leaf: %v\n", response.DataProof.Leaf.Hex()) + + // Access the message based on its type + if response.Message != nil { + switch m := response.Message.(type) { + case *rpc.ArbitraryMessage: + fmt.Println("Arbitrary Message:", m.BoundedData) + case *rpc.FungibleToken: + fmt.Println("Fungible Token AssetID:", m.AssetID.Hex(), "Amount:", m.Amount) + default: + fmt.Println("Unknown Message Type") + } + } else { + fmt.Println("Message: null") + } +} diff --git a/avail-go/examples/rpc/kate_queryRows/main.go b/avail-go/examples/rpc/kate_queryRows/main.go new file mode 100644 index 000000000..243ee2448 --- /dev/null +++ b/avail-go/examples/rpc/kate_queryRows/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "fmt" + + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/call" + "avail-go-sdk/src/sdk/tx" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + + appID := 0 + + // if app id is greater than 0 then it must be created before submitting data + if config.AppID != 0 { + appID = config.AppID + } + WaitFor := sdk.BlockFinalization + + blockHash, txHash, err := tx.SubmitData(api, config.Seed, appID, "my happy data", WaitFor) + if err != nil { + fmt.Printf("cannot submit data:%v", err) + } + fmt.Printf("Data submitted successfully with block hash: %v\n and ext hash:%v\n", blockHash.Hex(), txHash.Hex()) + + defer func() { + if r := recover(); r != nil { + fmt.Println("Recovered in main", r) + } + }() + myArr := make([]uint32, 1) + myArr[0] = 0 + response, err := call.Query_rows(api, myArr, blockHash) + if err != nil { + fmt.Println("Error calling api.Client.Call:", err) + return + } + + formattedResponse := make([][]string, len(response)) + + for i, innerSlice := range response { + formattedResponse[i] = make([]string, len(innerSlice)) + for j, num := range innerSlice { + formattedResponse[i][j] = sdk.FormatBN(num) + } + } + fmt.Println(formattedResponse) + +} diff --git a/avail-go/examples/staking_bond/main.go b/avail-go/examples/staking_bond/main.go new file mode 100644 index 000000000..f3a8adf67 --- /dev/null +++ b/avail-go/examples/staking_bond/main.go @@ -0,0 +1,31 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + Payee := sdk.Staked + + bondAmount := int64(1000) + + BlockHash, txHash, err := tx.Bond(api, config.Seed, WaitFor, bondAmount, sdk.Payee(Payee)) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) + sdk.EventParser(api, BlockHash, "Bond") +} diff --git a/avail-go/examples/staking_bond_extra/main.go b/avail-go/examples/staking_bond_extra/main.go new file mode 100644 index 000000000..0363f233a --- /dev/null +++ b/avail-go/examples/staking_bond_extra/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + + bondAmount := int64(1000) + + BlockHash, txHash, err := tx.BondExtra(api, config.Seed, WaitFor, bondAmount) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) +} diff --git a/avail-go/examples/staking_chill/main.go b/avail-go/examples/staking_chill/main.go new file mode 100644 index 000000000..0f3127019 --- /dev/null +++ b/avail-go/examples/staking_chill/main.go @@ -0,0 +1,27 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + + BlockHash, txHash, err := tx.Chill(api, config.Seed, WaitFor) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) +} diff --git a/avail-go/examples/staking_chill_other/main.go b/avail-go/examples/staking_chill_other/main.go new file mode 100644 index 000000000..5aebeed25 --- /dev/null +++ b/avail-go/examples/staking_chill_other/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + + stash := "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY" + BlockHash, txHash, err := tx.ChillOther(api, config.Seed, WaitFor, stash) + if err != nil { + fmt.Printf("cannot submit data:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) +} diff --git a/avail-go/examples/staking_nominate/main.go b/avail-go/examples/staking_nominate/main.go new file mode 100644 index 000000000..b6ddeb254 --- /dev/null +++ b/avail-go/examples/staking_nominate/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + + stash := []string{"5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY", "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"} + BlockHash, txHash, err := tx.Nominate(api, config.Seed, WaitFor, stash) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) +} diff --git a/avail-go/examples/staking_unbond/main.go b/avail-go/examples/staking_unbond/main.go new file mode 100644 index 000000000..a739caa17 --- /dev/null +++ b/avail-go/examples/staking_unbond/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + "avail-go-sdk/src/sdk/types" + "math/big" + + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + + bondAmount := new(big.Int) + bondAmount.SetString("100000000000000000000", 10) + + // Convert big.Int to types.UCompact + bondAmountUCompact := types.NewUCompact(bondAmount) + BlockHash, txHash, err := tx.Unbond(api, config.Seed, WaitFor, bondAmountUCompact) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) +} diff --git a/avail-go/examples/staking_validate/main.go b/avail-go/examples/staking_validate/main.go new file mode 100644 index 000000000..d184e4f8d --- /dev/null +++ b/avail-go/examples/staking_validate/main.go @@ -0,0 +1,27 @@ +package main + +import ( + "avail-go-sdk/src/config" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/tx" + "fmt" +) + +func main() { + config, err := config.LoadConfig() + if err != nil { + fmt.Printf("cannot load config:%v", err) + } + api, err := sdk.NewSDK(config.ApiURL) + if err != nil { + fmt.Printf("cannot create api:%v", err) + } + WaitFor := sdk.BlockInclusion + commission := 5 + BlockHash, txHash, err := tx.Validate(api, config.Seed, WaitFor, commission) + if err != nil { + fmt.Printf("cannot submit Transaction:%v", err) + } + fmt.Printf("Transaction submitted successfully with block hash: %v\n and ext hash:%v", BlockHash.Hex(), txHash.Hex()) + +} diff --git a/examples/go/go.mod b/avail-go/go.mod similarity index 74% rename from examples/go/go.mod rename to avail-go/go.mod index 055e8f9da..e4dfb0d5d 100644 --- a/examples/go/go.mod +++ b/avail-go/go.mod @@ -1,13 +1,13 @@ -module avail-gsrpc-examples +module avail-go-sdk -go 1.19 +go 1.21.0 require ( - github.com/centrifuge/go-substrate-rpc-client/v4 v4.0.12 + github.com/centrifuge/go-substrate-rpc-client/v4 v4.2.1 github.com/ethereum/go-ethereum v1.10.20 github.com/vedhavyas/go-subkey v1.0.4 github.com/vedhavyas/go-subkey/v2 v2.0.0 - golang.org/x/crypto v0.22.0 + golang.org/x/crypto v0.7.0 ) require ( @@ -25,11 +25,11 @@ require ( github.com/pierrec/xxHash v0.1.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rs/cors v1.8.2 // indirect - github.com/stretchr/objx v0.1.0 // indirect + github.com/stretchr/objx v0.1.1 // indirect github.com/stretchr/testify v1.7.2 // indirect - golang.org/x/sys v0.19.0 // indirect + golang.org/x/sys v0.6.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace github.com/centrifuge/go-substrate-rpc-client/v4 => github.com/availproject/go-substrate-rpc-client/v4 v4.1.0-avail-2.1.5-rc1 +// replace github.com/centrifuge/go-substrate-rpc-client/v4 => /Users/kailaskr/gsrpc diff --git a/avail-go/go.sum b/avail-go/go.sum new file mode 100644 index 000000000..d5460e81a --- /dev/null +++ b/avail-go/go.sum @@ -0,0 +1,85 @@ +github.com/ChainSafe/go-schnorrkel v1.0.0 h1:3aDA67lAykLaG1y3AOjs88dMxC88PgUuHRrLeDnvGIM= +github.com/ChainSafe/go-schnorrkel v1.0.0/go.mod h1:dpzHYVxLZcp8pjlV+O+UR8K0Hp/z7vcchBSbMBEhCw4= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo= +github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= +github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= +github.com/centrifuge/go-substrate-rpc-client/v4 v4.2.1 h1:io49TJ8IOIlzipioJc9pJlrjgdJvqktpUWYxVY5AUjE= +github.com/centrifuge/go-substrate-rpc-client/v4 v4.2.1/go.mod h1:k61SBXqYmnZO4frAJyH3iuqjolYrYsq79r8EstmklDY= +github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= +github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= +github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= +github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= +github.com/decred/base58 v1.0.4 h1:QJC6B0E0rXOPA8U/kw2rP+qiRJsUaE2Er+pYb3siUeA= +github.com/decred/base58 v1.0.4/go.mod h1:jJswKPEdvpFpvf7dsDvFZyLT22xZ9lWqEByX38oGd9E= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/ethereum/go-ethereum v1.10.20 h1:75IW830ClSS40yrQC1ZCMZCt5I+zU16oqId2SiQwdQ4= +github.com/ethereum/go-ethereum v1.10.20/go.mod h1:LWUN82TCHGpxB3En5HVmLLzPD7YSrEUFmFfN1nKkVN0= +github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= +github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64= +github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= +github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= +github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= +github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= +github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b h1:QrHweqAtyJ9EwCaGHBu1fghwxIPiopAHV06JlXrMHjk= +github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b/go.mod h1:xxLb2ip6sSUts3g1irPVHyk/DGslwQsNOo9I7smJfNU= +github.com/pierrec/xxHash v0.1.5 h1:n/jBpwTHiER4xYvK3/CdPVnLDPchj8eTJFFLUb4QHBo= +github.com/pierrec/xxHash v0.1.5/go.mod h1:w2waW5Zoa/Wc4Yqe0wgrIYAGKqRMf7czn2HNKXmuL+I= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= +github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= +github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= +github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= +github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= +github.com/vedhavyas/go-subkey v1.0.4 h1:QwjBZx4w7qXC2lmqol2jJfhaNXPI9BsgLZiMiCwqGDU= +github.com/vedhavyas/go-subkey v1.0.4/go.mod h1:aOIil/KS9hJlnr9ZSQKSoXdu/MbnkCxG4x9IOlLsMtI= +github.com/vedhavyas/go-subkey/v2 v2.0.0 h1:LemDIsrVtRSOkp0FA8HxP6ynfKjeOj3BY2U9UNfeDMA= +github.com/vedhavyas/go-subkey/v2 v2.0.0/go.mod h1:95aZ+XDCWAUUynjlmi7BtPExjXgXxByE0WfBwbmIRH4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/avail-go/src/config/config.go b/avail-go/src/config/config.go new file mode 100644 index 000000000..1915384a1 --- /dev/null +++ b/avail-go/src/config/config.go @@ -0,0 +1,59 @@ +package config + +import ( + "encoding/json" + "flag" + "fmt" + "io" + "log" + "os" +) + +type Config struct { + Seed string `json:"seed"` + ApiURL string `json:"api_url"` + Size int `json:"size"` + AppID int `json:"app_id"` + Dest string `json:"dest"` + Amount uint64 `json:"amount"` + SessionKeys string `json:"session_keys"` +} + +func (c *Config) GetConfig(configFileName string) error { + jsonFile, err := os.Open(configFileName) + if err != nil { + return err + } + defer jsonFile.Close() + + byteValue, err := io.ReadAll(jsonFile) + if err != nil { + return err + } + + err = json.Unmarshal(byteValue, c) + if err != nil { + return err + } + + return nil +} + +func LoadConfig() (*Config, error) { + var configJSON string + flag.StringVar(&configJSON, "config", "", "config json file") + flag.Parse() + + if configJSON == "" { + log.Println("No config file provided. Exiting...") + os.Exit(0) + } + + var config Config + err := config.GetConfig(configJSON) + if err != nil { + return nil, fmt.Errorf("cannot get config: %v", err) + } + + return &config, nil +} diff --git a/avail-go/src/extrinsic/era.go b/avail-go/src/extrinsic/era.go new file mode 100644 index 000000000..7e3e1e7d3 --- /dev/null +++ b/avail-go/src/extrinsic/era.go @@ -0,0 +1,163 @@ +package extrinsic + +import ( + "errors" + "math" + "strconv" + + "github.com/centrifuge/go-substrate-rpc-client/v4/scale" + "github.com/centrifuge/go-substrate-rpc-client/v4/types" +) + +// ExtrinsicEra indicates either a mortal or immortal extrinsic +type ExtrinsicEra struct { + IsImmortalEra bool + // AsImmortalEra ImmortalEra + IsMortalEra bool + AsMortalEra MortalEra +} + +func (e *ExtrinsicEra) Decode(decoder scale.Decoder) error { + first, err := decoder.ReadOneByte() + if err != nil { + return err + } + + if first == 0 { + e.IsImmortalEra = true + return nil + } + + second, err := decoder.ReadOneByte() + if err != nil { + return err + } + + encoded := uint64(first) + (uint64(second) << 8) + period := 2 << (encoded % (1 << 4)) + quantizeFactor := period >> 12 + + if quantizeFactor <= 1 { + quantizeFactor = 1 + } + + phase := (encoded >> 4) * uint64(quantizeFactor) + + if period >= 4 && phase < uint64(period) { + e.IsMortalEra = true + e.AsMortalEra = MortalEra{ + First: types.U64(period), + Second: types.U64(phase), + } + return nil + } + + return errors.New("invalid era") +} + +func (e ExtrinsicEra) Encode(encoder scale.Encoder) error { + if e.IsImmortalEra { + return encoder.PushByte(0) + } + + // let quantize_factor = (*period as u64 >> 12).max(1); + quantizeFactor := e.AsMortalEra.First >> 12 + + if quantizeFactor <= 1 { + quantizeFactor = 1 + } + + // let encoded = (period.trailing_zeros() - 1).max(1).min(15) as u16 | + // ((phase / quantize_factor) << 4) as u16; + trailingZeroes := getTrailingZeroes(e.AsMortalEra.First) - 1 + + if trailingZeroes <= 1 { + trailingZeroes = 1 + } + + if trailingZeroes >= 15 { + trailingZeroes = 15 + } + + r := types.U16((e.AsMortalEra.Second / quantizeFactor) << 4) + + encoded := trailingZeroes | r + + // encoded.encode_to(output); + return encoder.Encode(encoded) +} + +// MortalEra for an extrinsic, indicating period and phase +type MortalEra struct { + First types.U64 + Second types.U64 +} + +func NewMortalEra(currentBlock types.BlockNumber, blockHashCount types.U64) MortalEra { + // BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64; + np := getNextPowerOfTwo(blockHashCount, 2) + + var npb types.U64 + + if np > 2 { + npb = np / 2 + } + + // let period = period.checked_next_power_of_two().unwrap_or(1 << 16).max(4).min(1 << 16); + period := getNextPowerOfTwo(npb, 1<<16) + + if period <= 4 { + period = 4 + } + + if period >= 1<<16 { + period = 1 << 16 + } + + // let phase = current % period; + phase := types.U64(currentBlock) % period + + // let quantize_factor = (period >> 12).max(1); + quantizeFactor := period >> 12 + + if quantizeFactor <= 1 { + quantizeFactor = 1 + } + + // let quantized_phase = phase / quantize_factor * quantize_factor; + quantizedPhase := phase / quantizeFactor * quantizeFactor + + return MortalEra{period, quantizedPhase} +} + +func getNextPowerOfTwo(n types.U64, def types.U64) types.U64 { + bn := strconv.FormatInt(int64(n), 2) + numBits := len(bn) + + if (1 << (numBits - 1)) == n { + return n + } + + res := uint(1 << numBits) + + if res > math.MaxUint64 { + return def + } + + return types.U64(res) +} + +func getTrailingZeroes(n types.U64) types.U16 { + var count types.U16 + + for n > 0 { + if n%2 == 1 { + break + } + + n = n / 2 + count++ + } + + return count +} diff --git a/avail-go/src/extrinsic/extrinsic.go b/avail-go/src/extrinsic/extrinsic.go new file mode 100644 index 000000000..c1c9a84d6 --- /dev/null +++ b/avail-go/src/extrinsic/extrinsic.go @@ -0,0 +1,262 @@ +package extrinsic + +import ( + "bytes" + "encoding/json" + "fmt" + "math/big" + "strings" + + "github.com/centrifuge/go-substrate-rpc-client/v4/scale" + "github.com/centrifuge/go-substrate-rpc-client/v4/signature" + "github.com/centrifuge/go-substrate-rpc-client/v4/types" + "github.com/centrifuge/go-substrate-rpc-client/v4/types/codec" +) + +type SignatureOptions struct { + Era ExtrinsicEra + Nonce types.UCompact + Tip types.UCompact + SpecVersion types.U32 + GenesisHash types.Hash + BlockHash types.Hash + AppID types.UCompact + TransactionVersion types.U32 +} +type ExtrinsicSignatureV4 struct { + Signer types.MultiAddress + Signature types.MultiSignature + Era ExtrinsicEra // extra via system::CheckEra + Nonce types.UCompact // extra via system::CheckNonce (Compact where Index is u32)) + Tip types.UCompact // extra via balances::TakeFees (Compact where Balance is u128)) + AppID types.UCompact // Avail specific AppID +} + +const ( + ExtrinsicBitSigned = 0x80 + ExtrinsicBitUnsigned = 0 + ExtrinsicUnmaskVersion = 0x7f + ExtrinsicDefaultVersion = 1 + ExtrinsicVersionUnknown = 0 // v0 is unknown + ExtrinsicVersion1 = 1 + ExtrinsicVersion2 = 2 + ExtrinsicVersion3 = 3 + ExtrinsicVersion4 = 4 +) + +// Extrinsic is a piece of Args bundled into a block that expresses something from the "external" (i.e. off-chain) +// world. There are, broadly speaking, two types of extrinsic: transactions (which tend to be signed) and +// inherents (which don't). +type Extrinsic struct { + // Version is the encoded version flag (which encodes the raw transaction version and signing information in one byte) + Version byte + // Signature is the ExtrinsicSignatureV4, it's presence depends on the Version flag + Signature ExtrinsicSignatureV4 + // Method is the call this extrinsic wraps + Method types.Call +} + +// NewExtrinsic creates a new Extrinsic from the provided Call +func NewExtrinsic(c types.Call) Extrinsic { + return Extrinsic{ + Version: ExtrinsicVersion4, + Method: c, + } +} + +// UnmarshalJSON fills Extrinsic with the JSON encoded byte array given by bz +func (e *Extrinsic) UnmarshalJSON(bz []byte) error { + var tmp string + if err := json.Unmarshal(bz, &tmp); err != nil { + return err + } + + // HACK 11 Jan 2019 - before https://github.com/paritytech/substrate/pull/1388 + // extrinsics didn't have the length, cater for both approaches. This is very + // inconsistent with any other `Vec` implementation + var l types.UCompact + err := codec.DecodeFromHex(tmp, &l) + if err != nil { + return err + } + + prefix, err := codec.EncodeToHex(l) + if err != nil { + return err + } + + // determine whether length prefix is there + if strings.HasPrefix(tmp, prefix) { + return codec.DecodeFromHex(tmp, e) + } + + // not there, prepend with compact encoded length prefix + dec, err := codec.HexDecodeString(tmp) + if err != nil { + return err + } + length := types.NewUCompactFromUInt(uint64(len(dec))) + bprefix, err := codec.Encode(length) + if err != nil { + return err + } + bprefix = append(bprefix, dec...) + return codec.Decode(bprefix, e) +} + +// MarshalJSON returns a JSON encoded byte array of Extrinsic +func (e Extrinsic) MarshalJSON() ([]byte, error) { + s, err := codec.EncodeToHex(e) + if err != nil { + return nil, err + } + return json.Marshal(s) +} + +// IsSigned returns true if the extrinsic is signed +func (e Extrinsic) IsSigned() bool { + return e.Version&ExtrinsicBitSigned == ExtrinsicBitSigned +} + +// Type returns the raw transaction version (not flagged with signing information) +func (e Extrinsic) Type() uint8 { + return e.Version & ExtrinsicUnmaskVersion +} + +// Sign adds a signature to the extrinsic +func (e *Extrinsic) Sign(signer signature.KeyringPair, o SignatureOptions) error { + if e.Type() != ExtrinsicVersion4 { + return fmt.Errorf("unsupported extrinsic version: %v (isSigned: %v, type: %v)", e.Version, e.IsSigned(), e.Type()) + } + + mb, err := codec.Encode(e.Method) + if err != nil { + return err + } + + era := o.Era + if !o.Era.IsMortalEra { + era = ExtrinsicEra{IsImmortalEra: true} + } + + payload := ExtrinsicPayloadV3{ + Method: mb, + Era: era, + Nonce: o.Nonce, + Tip: o.Tip, + AppID: o.AppID, + SpecVersion: o.SpecVersion, + TransactionVersion: o.TransactionVersion, + GenesisHash: o.GenesisHash, + BlockHash: o.BlockHash, + } + + signerPubKey, err := types.NewMultiAddressFromAccountID(signer.PublicKey) + + if err != nil { + return err + } + + sig, err := payload.Sign(signer) + if err != nil { + return err + } + + extSig := ExtrinsicSignatureV4{ + Signer: signerPubKey, + Signature: types.MultiSignature{IsSr25519: true, AsSr25519: sig}, + Era: era, + Nonce: o.Nonce, + Tip: o.Tip, + AppID: o.AppID, + } + + e.Signature = extSig + + // mark the extrinsic as signed + e.Version |= ExtrinsicBitSigned + + return nil +} + +func (e *Extrinsic) Decode(decoder scale.Decoder) error { + // compact length encoding (1, 2, or 4 bytes) (may not be there for Extrinsics older than Jan 11 2019) + _, err := decoder.DecodeUintCompact() + if err != nil { + return err + } + + // version, signature bitmask (1 byte) + err = decoder.Decode(&e.Version) + if err != nil { + return err + } + + // signature + if e.IsSigned() { + if e.Type() != ExtrinsicVersion4 { + return fmt.Errorf("unsupported extrinsic version: %v (isSigned: %v, type: %v)", e.Version, e.IsSigned(), + e.Type()) + } + + err = decoder.Decode(&e.Signature) + if err != nil { + return err + } + } + + // call + err = decoder.Decode(&e.Method) + if err != nil { + return err + } + + return nil +} + +func (e Extrinsic) Encode(encoder scale.Encoder) error { + if e.Type() != ExtrinsicVersion4 { + return fmt.Errorf("unsupported extrinsic version: %v (isSigned: %v, type: %v)", e.Version, e.IsSigned(), + e.Type()) + } + + // create a temporary buffer that will receive the plain encoded transaction (version, signature (optional), + // method/call) + var bb = bytes.Buffer{} + tempEnc := scale.NewEncoder(&bb) + + // encode the version of the extrinsic + err := tempEnc.Encode(e.Version) + if err != nil { + return err + } + + // encode the signature if signed + if e.IsSigned() { + err = tempEnc.Encode(e.Signature) + if err != nil { + return err + } + } + + // encode the method + err = tempEnc.Encode(e.Method) + if err != nil { + return err + } + + // take the temporary buffer to determine length, write that as prefix + eb := bb.Bytes() + err = encoder.EncodeUintCompact(*big.NewInt(0).SetUint64(uint64(len(eb)))) + if err != nil { + return err + } + + // write the actual encoded transaction + err = encoder.Write(eb) + if err != nil { + return err + } + + return nil +} diff --git a/avail-go/src/extrinsic/payload.go b/avail-go/src/extrinsic/payload.go new file mode 100644 index 000000000..6229372cb --- /dev/null +++ b/avail-go/src/extrinsic/payload.go @@ -0,0 +1,158 @@ +package extrinsic + +import ( + "fmt" + + "github.com/centrifuge/go-substrate-rpc-client/v4/signature" + "github.com/centrifuge/go-substrate-rpc-client/v4/types" + "github.com/centrifuge/go-substrate-rpc-client/v4/types/codec" + "github.com/vedhavyas/go-subkey/scale" +) + +type ExtrinsicPayloadV3 struct { + Method types.BytesBare + Era ExtrinsicEra // extra via system::CheckEra + Nonce types.UCompact // extra via system::CheckNonce (Compact where Index is u32) + Tip types.UCompact // extra via balances::TakeFees (Compact where Balance is u128) + AppID types.UCompact + SpecVersion types.U32 // additional via system::CheckVersion + TransactionVersion types.U32 + GenesisHash types.Hash // additional via system::CheckGenesis + BlockHash types.Hash // additional via system::CheckEra +} + +// Sign the extrinsic payload with the given derivation path +func (e ExtrinsicPayloadV3) Sign(signer signature.KeyringPair) (types.Signature, error) { + b, err := codec.Encode(e) + if err != nil { + return types.Signature{}, err + } + + sig, err := signature.Sign(b, signer.URI) + return types.NewSignature(sig), err +} + +// Encode implements encoding for ExtrinsicPayloadV3, which just unwraps the bytes of ExtrinsicPayloadV3 without +// adding a compact length prefix +func (e ExtrinsicPayloadV3) Encode(encoder scale.Encoder) error { + err := encoder.Encode(e.Method) + if err != nil { + return err + } + + err = encoder.Encode(e.Era) + if err != nil { + return err + } + + err = encoder.Encode(e.Nonce) + if err != nil { + return err + } + + err = encoder.Encode(e.Tip) + if err != nil { + return err + } + + err = encoder.Encode(e.SpecVersion) + if err != nil { + return err + } + + err = encoder.Encode(e.GenesisHash) + if err != nil { + return err + } + + err = encoder.Encode(e.BlockHash) + if err != nil { + return err + } + err = encoder.Encode(e.AppID) + if err != nil { + return err + } + err = encoder.Encode(e.TransactionVersion) + if err != nil { + return err + } + + return nil +} + +// Decode does nothing and always returns an error. ExtrinsicPayloadV3 is only used for encoding, not for decoding +func (e *ExtrinsicPayloadV3) Decode(decoder scale.Decoder) error { + return fmt.Errorf("decoding of ExtrinsicPayloadV3 is not supported") +} + +type ExtrinsicPayloadV4 struct { + ExtrinsicPayloadV3 + TransactionVersion types.U32 + AppID types.UCompact +} + +// Sign the extrinsic payload with the given derivation path +func (e ExtrinsicPayloadV4) Sign(signer signature.KeyringPair) (types.Signature, error) { + b, err := codec.Encode(e) + if err != nil { + return types.Signature{}, err + } + + sig, err := signature.Sign(b, signer.URI) + return types.NewSignature(sig), err +} + +func (e ExtrinsicPayloadV4) Encode(encoder scale.Encoder) error { + err := encoder.Encode(e.Method) + if err != nil { + return err + } + + err = encoder.Encode(e.Era) + if err != nil { + return err + } + + err = encoder.Encode(e.Nonce) + if err != nil { + return err + } + + err = encoder.Encode(e.Tip) + if err != nil { + return err + } + + err = encoder.Encode(e.AppID) + if err != nil { + return err + } + + err = encoder.Encode(e.SpecVersion) + if err != nil { + return err + } + + err = encoder.Encode(e.TransactionVersion) + if err != nil { + return err + } + + err = encoder.Encode(e.GenesisHash) + if err != nil { + return err + } + + err = encoder.Encode(e.BlockHash) + if err != nil { + return err + } + + return nil +} + +// Decode does nothing and always returns an error. ExtrinsicPayloadV4 is only used for encoding, not for decoding +func (e *ExtrinsicPayloadV4) Decode(decoder scale.Decoder) error { + return fmt.Errorf("decoding of ExtrinsicPayloadV4 is not supported") +} diff --git a/avail-go/src/header/header.go b/avail-go/src/header/header.go new file mode 100644 index 000000000..06fb8266f --- /dev/null +++ b/avail-go/src/header/header.go @@ -0,0 +1,74 @@ +package header + +import ( + avail "avail-go-sdk/src/sdk/types" + + "github.com/centrifuge/go-substrate-rpc-client/v4/scale" + "github.com/centrifuge/go-substrate-rpc-client/v4/types" +) + +type DataLookupIndexItem struct { + AppId avail.UCompact `json:"appId"` + Start avail.UCompact `json:"start"` +} +type DataLookup struct { + Size avail.UCompact `json:"size"` + Index []DataLookupIndexItem `json:"index"` +} + +type KateCommitment struct { + Rows avail.UCompact `json:"rows"` + Cols avail.UCompact `json:"cols"` + Commitment []types.U8 `json:"commitment"` + DataRoot types.Hash `json:"dataRoot"` +} + +type V3HeaderExtension struct { + AppLookup DataLookup `json:"appLookup"` + Commitment KateCommitment `json:"commitment"` +} + +type HeaderExtensionEnum struct { + V3 V3HeaderExtension `json:"V3"` +} + +type Header struct { + ParentHash types.Hash `json:"parentHash"` + Number types.BlockNumber `json:"number"` + StateRoot types.Hash `json:"stateRoot"` + ExtrinsicsRoot types.Hash `json:"extrinsicsRoot"` + Digest types.Digest `json:"digest"` + Extension HeaderExtensionEnum `json:"extension"` +} + +func (m HeaderExtensionEnum) Encode(encoder scale.Encoder) error { + var err, err1 error + + err = encoder.PushByte(2) + + if err != nil { + return err + } + err1 = encoder.Encode(m.V3) + if err1 != nil { + return err1 + } + return nil +} + +func (m *HeaderExtensionEnum) Decode(decoder scale.Decoder) error { + b, err := decoder.ReadOneByte() + + if err != nil { + return err + } + + if b == 2 { + err = decoder.Decode(&m.V3) + } + if err != nil { + return err + } + + return nil +} diff --git a/avail-go/src/rpc/block.go b/avail-go/src/rpc/block.go new file mode 100644 index 000000000..74ef4479b --- /dev/null +++ b/avail-go/src/rpc/block.go @@ -0,0 +1,91 @@ +package rpc + +import ( + "avail-go-sdk/src/extrinsic" + "avail-go-sdk/src/header" + + "github.com/centrifuge/go-substrate-rpc-client/v4/client" + "github.com/centrifuge/go-substrate-rpc-client/v4/types" +) + +type SignedBlock struct { + Block Block `json:"block"` + Justification types.Justification `json:"justification"` +} + +// Block encoded with header and extrinsics +type Block struct { + Header header.Header `json:"header"` + Extrinsics []extrinsic.Extrinsic +} + +// GetBlock returns the header and body of the relay chain block with the given hash +func GetAvailBlock(blockHash types.Hash, client1 client.Client) (*SignedBlock, error) { + return getBlock(&blockHash, client1) +} + +func GetAvailBlockLatest(client1 client.Client) (*SignedBlock, error) { + return getBlock(nil, client1) +} + +func getBlock(blockHash *types.Hash, client1 client.Client) (*SignedBlock, error) { + var SignedBlock SignedBlock + err := client.CallWithBlockHash(client1, &SignedBlock, "chain_getBlock", blockHash) + if err != nil { + return nil, err + } + return &SignedBlock, err +} + +func GetFinalizedHead(client client.Client) (types.Hash, error) { + var res string + + err := client.Call(&res, "chain_getFinalizedHead") + if err != nil { + return types.Hash{}, err + } + + return types.NewHashFromHexString(res) +} + +func getBlockHash(client client.Client, blockNumber *uint64) (types.Hash, error) { + var res string + var err error + + if blockNumber == nil { + err = client.Call(&res, "chain_getBlockHash") + } else { + err = client.Call(&res, "chain_getBlockHash", *blockNumber) + } + + if err != nil { + return types.Hash{}, err + } + + return types.NewHashFromHexString(res) +} + +func GetBlockHash(client client.Client, blockNumber uint64) (types.Hash, error) { + return getBlockHash(client, &blockNumber) +} + +func GetBlockHashLatest(client client.Client) (types.Hash, error) { + return getBlockHash(client, nil) +} + +func getHeader(client1 client.Client, blockHash *types.Hash) (*header.Header, error) { + var Header header.Header + err := client.CallWithBlockHash(client1, &Header, "chain_getHeader", blockHash) + if err != nil { + return nil, err + } + return &Header, err +} + +func GetHeaderLatest(client1 client.Client) (*header.Header, error) { + return getHeader(client1, nil) +} + +func GetHeader(client1 client.Client, blockHash types.Hash) (*header.Header, error) { + return getHeader(client1, &blockHash) +} diff --git a/examples/go/query_proof/main.go b/avail-go/src/rpc/kate.go similarity index 51% rename from examples/go/query_proof/main.go rename to avail-go/src/rpc/kate.go index 62466962a..a9523d595 100644 --- a/examples/go/query_proof/main.go +++ b/avail-go/src/rpc/kate.go @@ -1,17 +1,11 @@ -package main +package rpc import ( - "avail-gsrpc-examples/internal/config" - "avail-gsrpc-examples/internal/extrinsics" "encoding/json" - "flag" "fmt" - "log" "math/big" - "os" "strings" - gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" "github.com/centrifuge/go-substrate-rpc-client/v4/types" ) @@ -48,7 +42,7 @@ func (g *GDataProof) UnmarshalJSON(data []byte) error { return err } - fmt.Println("RawScalarString:", rawScalarString) + fmt.Println("RawScalarString:", rawScalarString) // Strip '0x' prefix and convert to big.Int trimmedScalarString := strings.TrimPrefix(rawScalarString, "0x") @@ -57,17 +51,18 @@ func (g *GDataProof) UnmarshalJSON(data []byte) error { fmt.Printf("Failed to convert RawScalar to big.Int, string was: %s\n", trimmedScalarString) return fmt.Errorf("invalid RawScalar format") } - g.RawScalar = *rawScalar + g.RawScalar = *rawScalar // Unmarshal Proof var proof [48]byte if err := json.Unmarshal(tupleData[1], &proof); err != nil { return err } - g.Proof = GProof{proof} + g.Proof = GProof{proof} return nil } + func formatBigIntWithCommas(b *big.Int) string { if b == nil { return "" @@ -99,71 +94,57 @@ func formatBigIntWithCommas(b *big.Int) string { } func (g *GDataProof) MarshalJSON() ([]byte, error) { - rawScalarStr := formatBigIntWithCommas(&g.RawScalar) + rawScalarStr := formatBigIntWithCommas(&g.RawScalar) proofHex := fmt.Sprintf("0x%x", g.Proof.Data) return json.Marshal([]interface{}{rawScalarStr, proofHex}) } -func main() { - var configJSON string - var config config.Config - flag.StringVar(&configJSON, "config", "", "config json file") - flag.Parse() +type ProofResponse struct { + DataProof DataProof + Message Message // Interface to capture different message types +} - if configJSON == "" { - log.Println("No config file provided. Exiting...") - os.Exit(0) - } +type TxDataRoot struct { + DataRoot types.Hash + BlobRoot types.Hash + BridgeRoot types.Hash +} - err := config.GetConfig(configJSON) - if err != nil { - panic(fmt.Sprintf("cannot get config:%v", err)) - } - api, err := gsrpc.NewSubstrateAPI(config.ApiURL) - if err != nil { - panic(fmt.Sprintf("cannot create api client:%v", err)) - } - if api == nil || api.Client == nil { - log.Fatal("API client is not properly initialized") - } +// DataProof struct represents the data proof response +type DataProof struct { + Roots TxDataRoot + Proof []types.Hash + NumberOfLeaves uint32 // Change to uint32 to match Rust u32 + LeafIndex uint32 // Change to uint32 to match Rust u32 + Leaf types.Hash +} - var finalizedBlockCh = make(chan types.Hash) - go func() { - err = extrinsics.SubmitData(api, "data", config.Seed, 1, finalizedBlockCh) - if err != nil { - panic(fmt.Sprintf("cannot submit data:%v", err)) - } - }() - - // block hash to query proof - blockHash := <-finalizedBlockCh - fmt.Printf("Transaction included in finalized block: %v\n", blockHash.Hex()) - h, err := types.NewHashFromHexString(blockHash.Hex()) - if err != nil { - panic(fmt.Sprintf("cannot create api client:%v", err)) - } +// Message interface represents the enum variants +type Message interface { + isMessage() +} - defer func() { - if r := recover(); r != nil { - fmt.Println("Recovered in main", r) - } - }() - var result []GDataProof - testCell := []Cell{NewCell(0, 0), NewCell(0, 1)} +type BoundedData struct { + Data []byte +} - testCellJSON, _ := json.Marshal(testCell) - fmt.Println("Test Cell JSON:", string(testCellJSON)) +// BoundedDataMaxLen is the maximum length for the bounded data +const BoundedDataMaxLen = 32 // Adjust the maximum length as needed - err = api.Client.Call(&result, "kate_queryProof", testCell, h) - if err != nil { - fmt.Println("Error calling api.Client.Call:", err) - } +// ArbitraryMessage struct represents the ArbitraryMessage variant +type ArbitraryMessage struct { + BoundedData +} - resultJSON, err := json.Marshal(result) - if err != nil { - fmt.Println("Error marshalling result to JSON:", err) - return - } +func (a *ArbitraryMessage) isMessage() {} - fmt.Println(string(resultJSON)) +// FungibleToken struct represents the FungibleToken variant +type FungibleToken struct { + AssetID types.Hash + Amount uint128 // Define uint128 type as needed } + +func (f *FungibleToken) isMessage() {} + +type uint128 uint64 + diff --git a/avail-go/src/rpc/submit.go b/avail-go/src/rpc/submit.go new file mode 100644 index 000000000..e8da669ea --- /dev/null +++ b/avail-go/src/rpc/submit.go @@ -0,0 +1,87 @@ +package rpc + +import ( + "avail-go-sdk/src/extrinsic" + "context" + "fmt" + "sync" + + "github.com/centrifuge/go-substrate-rpc-client/v4/client" + "github.com/centrifuge/go-substrate-rpc-client/v4/config" + gethrpc "github.com/centrifuge/go-substrate-rpc-client/v4/gethrpc" + "github.com/centrifuge/go-substrate-rpc-client/v4/types" + "github.com/centrifuge/go-substrate-rpc-client/v4/types/codec" +) + +type ExtrinsicStatusSubscription struct { + sub *gethrpc.ClientSubscription + channel chan types.ExtrinsicStatus + quitOnce sync.Once // ensures quit is closed once +} + +// Chan returns the subscription channel. +// +// The channel is closed when Unsubscribe is called on the subscription. +func (s *ExtrinsicStatusSubscription) Chan() <-chan types.ExtrinsicStatus { + return s.channel +} + +// Err returns the subscription error channel. The intended use of Err is to schedule +// resubscription when the client connection is closed unexpectedly. +// +// The error channel receives a value when the subscription has ended due +// to an error. The received error is nil if Close has been called +// on the underlying client and no other error has occurred. +// +// The error channel is closed when Unsubscribe is called on the subscription. +func (s *ExtrinsicStatusSubscription) Err() <-chan error { + return s.sub.Err() +} + +// Unsubscribe unsubscribes the notification and closes the error channel. +// It can safely be called more than once. +func (s *ExtrinsicStatusSubscription) Unsubscribe() { + s.sub.Unsubscribe() + s.quitOnce.Do(func() { + close(s.channel) + }) +} + +// SubmitAndWatchExtrinsic will submit and subscribe to watch an extrinsic until unsubscribed, returning a subscription +// that will receive server notifications containing the extrinsic status updates. +func SubmitAndWatchExtrinsic(xt extrinsic.Extrinsic, client client.Client) (*ExtrinsicStatusSubscription, error) { //nolint:lll + ctx, cancel := context.WithTimeout(context.Background(), config.Default().SubscribeTimeout) + defer cancel() + + c := make(chan types.ExtrinsicStatus) + + enc, err := codec.EncodeToHex(xt) + if err != nil { + return nil, err + } + + sub, err := client.Subscribe(ctx, "author", "submitAndWatchExtrinsic", "unwatchExtrinsic", "extrinsicUpdate", + c, enc) + if err != nil { + return nil, err + } + + return &ExtrinsicStatusSubscription{sub: sub, channel: c}, nil +} + +func SubmitExtrinsic(xt extrinsic.Extrinsic, client client.Client) (types.Hash, error) { + enc, err := codec.EncodeToHex(xt) + + if err != nil { + return types.Hash{}, err + } + fmt.Println(enc) + + var res string + err = client.Call(&res, "author_submitExtrinsic", enc) + if err != nil { + return types.Hash{}, err + } + + return types.NewHashFromHexString(res) +} diff --git a/avail-go/src/sdk/call/call.go b/avail-go/src/sdk/call/call.go new file mode 100644 index 000000000..797f2a410 --- /dev/null +++ b/avail-go/src/sdk/call/call.go @@ -0,0 +1,54 @@ +package call + +import ( + "avail-go-sdk/src/rpc" + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/types" + "encoding/hex" + "fmt" +) + +func Query_proof(api *sdk.SubstrateAPI, testCell []rpc.Cell, blockHash types.Hash) ([]rpc.GDataProof, error) { + var result []rpc.GDataProof + err := api.Client.Call(&result, "kate_queryProof", testCell, blockHash) + if err != nil { + return nil, err + } + return result, nil +} + +func Author_Rotate(api *sdk.SubstrateAPI) ([]byte, error) { + var result string + err := api.Client.Call(&result, "author_rotateKeys", nil) + if err != nil { + return nil, err + } + if len(result) > 2 && result[:2] == "0x" { + result = result[2:] + } + + // Decode the hex string + decodedResult, err := hex.DecodeString(result) + if err != nil { + panic(fmt.Errorf("failed to decode result: %w", err)) + } + return decodedResult, nil +} + +func Query_rows(api *sdk.SubstrateAPI, arr []uint32, h types.Hash) ([][]sdk.BigInt, error) { + var response [][]sdk.BigInt + err := api.Client.Call(&response, "kate_queryRows", arr, h) + if err != nil { + return nil, err + } + return response, nil +} + +func Query_dataproof(api *sdk.SubstrateAPI, transactionIndex types.U32, h types.Hash) (rpc.ProofResponse, error) { + var response rpc.ProofResponse + err := api.Client.Call(&response, "kate_queryDataProof", transactionIndex, h) + if err != nil { + return rpc.ProofResponse{}, err + } + return response, nil +} diff --git a/avail-go/src/sdk/callExtrinsic.go b/avail-go/src/sdk/callExtrinsic.go new file mode 100644 index 000000000..dbb890cf7 --- /dev/null +++ b/avail-go/src/sdk/callExtrinsic.go @@ -0,0 +1,160 @@ +package sdk + +import ( + "avail-go-sdk/src/extrinsic" + "avail-go-sdk/src/rpc" + "encoding/hex" + "fmt" + "log" + "strings" + "time" + + "github.com/centrifuge/go-substrate-rpc-client/v4/signature" + "github.com/centrifuge/go-substrate-rpc-client/v4/types" + "github.com/ethereum/go-ethereum/common/hexutil" + "golang.org/x/crypto/blake2b" +) + +func NewExtrinsic(api *SubstrateAPI, ext_call string, keyring signature.KeyringPair, AppID int, arg ...interface{}) (types.Hash, error) { + meta, err := api.RPC.State.GetMetadataLatest() + if err != nil { + return types.Hash{}, err + } + call, err := types.NewCall(meta, ext_call, arg...) + if err != nil { + return types.Hash{}, err + } + ext := extrinsic.NewExtrinsic(call) + genesisHash, err := api.RPC.Chain.GetBlockHash(0) + if err != nil { + return types.Hash{}, err + } + rv, err := api.RPC.State.GetRuntimeVersionLatest() + if err != nil { + return types.Hash{}, err + } + key, err := types.CreateStorageKey(meta, "System", "Account", keyring.PublicKey) + if err != nil { + return types.Hash{}, err + } + + var accountInfo types.AccountInfo + ok, err := api.RPC.State.GetStorageLatest(key, &accountInfo) + if err != nil || !ok { + return types.Hash{}, err + } + nonce := uint32(accountInfo.Nonce) + options := extrinsic.SignatureOptions{ + BlockHash: genesisHash, + Era: extrinsic.ExtrinsicEra{IsMortalEra: false}, + GenesisHash: genesisHash, + Nonce: types.NewUCompactFromUInt(uint64(nonce)), + SpecVersion: rv.SpecVersion, + Tip: types.NewUCompactFromUInt(100), + AppID: types.NewUCompactFromUInt(uint64(AppID)), + TransactionVersion: rv.TransactionVersion, + } + err = ext.Sign(keyring, options) + if err != nil { + panic(fmt.Sprintf("cannot sign:%v", err)) + } + hash, err := rpc.SubmitExtrinsic(ext, api.Client) + if err != nil { + panic(fmt.Sprintf("cannot submit extrinsic:%v", err)) + } + + fmt.Printf("Data submitted using APPID: %v \n", AppID) + return hash, nil +} + +func NewExtrinsicWatch(api *SubstrateAPI, ext_call string, keyring signature.KeyringPair, final chan types.Hash, txHash chan types.Hash, AppID int, WaitForInclusion WaitFor, arg ...interface{}) error { + meta, err := api.RPC.State.GetMetadataLatest() + if err != nil { + return err + } + call, err := types.NewCall(meta, ext_call, arg...) + if err != nil { + return err + } + ext := extrinsic.NewExtrinsic(call) + genesisHash, err := api.RPC.Chain.GetBlockHash(0) + if err != nil { + return err + } + rv, err := api.RPC.State.GetRuntimeVersionLatest() + if err != nil { + return err + } + key, err := types.CreateStorageKey(meta, "System", "Account", keyring.PublicKey) + if err != nil { + return err + } + + var accountInfo types.AccountInfo + ok, err := api.RPC.State.GetStorageLatest(key, &accountInfo) + if err != nil || !ok { + return err + } + nonce := uint32(accountInfo.Nonce) + options := extrinsic.SignatureOptions{ + BlockHash: genesisHash, + Era: extrinsic.ExtrinsicEra{IsMortalEra: false}, + GenesisHash: genesisHash, + Nonce: types.NewUCompactFromUInt(uint64(nonce)), + SpecVersion: rv.SpecVersion, + Tip: types.NewUCompactFromUInt(100), + AppID: types.NewUCompactFromUInt(uint64(AppID)), + TransactionVersion: rv.TransactionVersion, + } + err = ext.Sign(keyring, options) + if err != nil { + panic(fmt.Sprintf("cannot sign:%v", err)) + } + + go func() { + enc, _ := EncodeToHex(ext) + + cleanedHexString := strings.TrimPrefix(enc, "0x") + bytes, err := hex.DecodeString(cleanedHexString) + if err != nil { + log.Fatal(err) + } + hash := blake2b.Sum256(bytes) + ext_z := hexutil.Encode(hash[:]) + hash, err = NewHashFromHexString(ext_z) + if err != nil { + log.Fatal(err) + } + txHash <- hash + }() + + sub, err := rpc.SubmitAndWatchExtrinsic(ext, api.Client) + if err != nil { + panic(fmt.Sprintf("cannot submit extrinsic:%v", err)) + } + + fmt.Printf("Transaction being submitted .... ⏳Waiting for block inclusion..") + + defer sub.Unsubscribe() + timeout := time.After(200 * time.Second) + for { + select { + case status := <-sub.Chan(): + switch WaitForInclusion { + case BlockInclusion: + if status.IsInBlock { + final <- status.AsInBlock + return err + } + case BlockFinalization: + if status.IsFinalized { + final <- status.AsFinalized + return err + } + } + case <-timeout: + fmt.Printf("timeout of 200 seconds reached without getting finalized status for extrinsic") + return err + } + } +} diff --git a/avail-go/src/sdk/events.go b/avail-go/src/sdk/events.go new file mode 100644 index 000000000..a95d946e8 --- /dev/null +++ b/avail-go/src/sdk/events.go @@ -0,0 +1,525 @@ +package sdk + +import ( + "encoding/hex" + "fmt" + + "log" + "math/big" + + "github.com/centrifuge/go-substrate-rpc-client/v4/registry" + "github.com/centrifuge/go-substrate-rpc-client/v4/registry/parser" + retriever "github.com/centrifuge/go-substrate-rpc-client/v4/registry/retriever" + "github.com/centrifuge/go-substrate-rpc-client/v4/registry/state" + "github.com/centrifuge/go-substrate-rpc-client/v4/types" + "github.com/vedhavyas/go-subkey" +) + +func EventParser(api *SubstrateAPI, h types.Hash, eventParse string) { + retriever, err := retriever.NewDefaultEventRetriever(state.NewEventProvider(api.RPC.State), api.RPC.State) + if err != nil { + log.Printf("Couldn't create event retriever") + } + + events, err := retriever.GetEvents(h) + + if err != nil { + log.Printf("Couldn't retrieve events") + } + + switch eventParse { + case "DataSubmitted": + ParseDataSubmitted(events) + case "ApplicationKeyCreated": + parseApplicationKeyCreated(events) + case "ApplicationKeySet": + parseApplicationKeySet(events) + case "BalanceTransfer": + parseBalanceTransfer(events) + case "Bond": + parseBond(events) + } +} +func ParseDataSubmitted(events []*parser.Event) { + for _, event := range events { + if event.Name == "DataAvailability.DataSubmitted" { + from, _ := registry.ProcessDecodedFieldValue[*types.AccountID]( + event.Fields, + func(fieldIndex int, field *registry.DecodedField) bool { + + return field.Name == "sp_core.crypto.AccountId32.who" + }, + func(value any) (*types.AccountID, error) { + fields, ok := value.(registry.DecodedFields) + + if !ok { + return nil, fmt.Errorf("unexpected value: %v", value) + } + + accByteSlice, err := registry.GetDecodedFieldAsSliceOfType[types.U8](fields, func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 0 + }) + + if err != nil { + return nil, err + } + + var accBytes []byte + + for _, accByte := range accByteSlice { + accBytes = append(accBytes, byte(accByte)) + } + + return types.NewAccountID(accBytes) + }, + ) + // a := from.ToHexString() + + // // add, _ := types.NewAddressFromHexAccountID(a) + // fmt.Println(from) + // fmt.Printf("from address read from event: %s \n", a) + has := subkey.SS58Encode(from.ToBytes(), 42) + fmt.Printf("from address read from event: %s \n", has) + dataHash, err := registry.ProcessDecodedFieldValue[*types.Hash]( + event.Fields, + func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 1 + }, + func(value any) (*types.Hash, error) { + fields, ok := value.(registry.DecodedFields) + if !ok { + return nil, fmt.Errorf("unexpected value: %v", value) + } + + hashByteSlice, err := registry.GetDecodedFieldAsSliceOfType[types.U8](fields, func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 0 + }) + + if err != nil { + return nil, err + } + + var hashBytes []byte + for _, hashByte := range hashByteSlice { + hashBytes = append(hashBytes, byte(hashByte)) + } + + hash := types.NewHash(hashBytes) + return &hash, nil + }, + ) + if err != nil { + fmt.Printf("DataHash parsing err: %s\n", err.Error()) + } else if dataHash == nil { + fmt.Println("DataHash is nil") + } else { + fmt.Printf("DataHash read from event: %s \n", dataHash.Hex()) + } + + } + parseTransactionFee(event) + } +} + +func parseTransactionFee(event *parser.Event) { + if event.Name == "TransactionPayment.TransactionFeePaid" { + + amount, err := registry.GetDecodedFieldAsType[types.U128]( + event.Fields, + func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 1 + }, + ) + if err != nil { + fmt.Printf("Amount parsing err: %s\n", err.Error()) + } + fmt.Printf("Actual Fee from TransactionPayment.TransactionFeePaid event: %s \n", convInt(amount.String())) + if err != nil { + fmt.Printf("Balances.Deposit.Who: %s\n", err.Error()) + } + } + +} + +func parseApplicationKeyCreated(events []*parser.Event) { + for _, event := range events { + if event.Name == "DataAvailability.ApplicationKeyCreated" { + owner, _ := registry.ProcessDecodedFieldValue[*types.AccountID]( + event.Fields, + func(fieldIndex int, field *registry.DecodedField) bool { + + return field.Name == "sp_core.crypto.AccountId32.owner" + }, + func(value any) (*types.AccountID, error) { + fields, ok := value.(registry.DecodedFields) + + if !ok { + return nil, fmt.Errorf("unexpected value: %v", value) + } + + accByteSlice, err := registry.GetDecodedFieldAsSliceOfType[types.U8](fields, func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 0 + }) + + if err != nil { + return nil, err + } + + var accBytes []byte + + for _, accByte := range accByteSlice { + accBytes = append(accBytes, byte(accByte)) + } + + return types.NewAccountID(accBytes) + }, + ) + // a := from.ToHexString() + + // // add, _ := types.NewAddressFromHexAccountID(a) + // fmt.Println(from) + // fmt.Printf("from address read from event: %s \n", a) + has := subkey.SS58Encode(owner.ToBytes(), 42) + fmt.Printf("from address read from event: %s \n", has) + + id, err := registry.ProcessDecodedFieldValue[types.UCompact]( + event.Fields, + func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 2 + }, + func(value any) (types.UCompact, error) { + fields, ok := value.(registry.DecodedFields) + if !ok { + return types.NewUCompact(big.NewInt(0)), fmt.Errorf("unexpected value type: %T", value) + } + + // Assuming the UCompact value is the first (and only) field in this struct + ucompact, err := registry.GetDecodedFieldAsType[types.UCompact](fields, func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 0 + }) + + if err != nil { + return types.NewUCompact(big.NewInt(0)), fmt.Errorf("error getting UCompact field: %w", err) + } + + return ucompact, nil + }, + ) + if err != nil { + fmt.Printf("AppId parsing err: %s\n", err.Error()) + } else { + fmt.Println(id.Int64()) + } + + appKey, err := registry.ProcessDecodedFieldValue[types.Bytes]( + event.Fields, + func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 0 // Adjust this index if needed + }, + func(value any) (types.Bytes, error) { + fields, ok := value.(registry.DecodedFields) + if !ok { + return nil, fmt.Errorf("unexpected value type: %T", value) + } + + // The BoundedVec is likely represented as a slice of U8 + byteSlice, err := registry.GetDecodedFieldAsSliceOfType[types.U8](fields, func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 0 + }) + + if err != nil { + return nil, fmt.Errorf("error getting byte slice: %w", err) + } + + // Convert the slice of U8 to types.Bytes + bytes := make(types.Bytes, len(byteSlice)) + for i, b := range byteSlice { + bytes[i] = byte(b) + } + + return bytes, nil + }, + ) + + if err != nil { + fmt.Printf("AppKey parsing err: %s\n", err.Error()) + } else { + // Convert Bytes to string for printing + stringValue := string(appKey) + fmt.Printf("AppKey from event (as string): %s\n", stringValue) + + // If you also want to see the hex representation: + hexString := hex.EncodeToString(appKey) + fmt.Printf("AppKey from event (as hex): 0x%s\n", hexString) + } + + } + parseTransactionFee(event) + } +} + +func parseApplicationKeySet(events []*parser.Event) { + for _, event := range events { + if event.Name == "DataAvailability.ApplicationKeySet" { + owner, _ := registry.ProcessDecodedFieldValue[*types.AccountID]( + event.Fields, + func(fieldIndex int, field *registry.DecodedField) bool { + + return field.Name == "sp_core.crypto.AccountId32.owner" + }, + func(value any) (*types.AccountID, error) { + fields, ok := value.(registry.DecodedFields) + + if !ok { + return nil, fmt.Errorf("unexpected value: %v", value) + } + + accByteSlice, err := registry.GetDecodedFieldAsSliceOfType[types.U8](fields, func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 0 + }) + + if err != nil { + return nil, err + } + + var accBytes []byte + + for _, accByte := range accByteSlice { + accBytes = append(accBytes, byte(accByte)) + } + + return types.NewAccountID(accBytes) + }, + ) + // a := from.ToHexString() + + // // add, _ := types.NewAddressFromHexAccountID(a) + // fmt.Println(from) + // fmt.Printf("from address read from event: %s \n", a) + has := subkey.SS58Encode(owner.ToBytes(), 42) + fmt.Printf("from address read from event: %s \n", has) + + id, err := registry.ProcessDecodedFieldValue[types.UCompact]( + event.Fields, + func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 2 + }, + func(value any) (types.UCompact, error) { + fields, ok := value.(registry.DecodedFields) + if !ok { + return types.NewUCompact(big.NewInt(0)), fmt.Errorf("unexpected value type: %T", value) + } + + // Assuming the UCompact value is the first (and only) field in this struct + ucompact, err := registry.GetDecodedFieldAsType[types.UCompact](fields, func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 0 + }) + + if err != nil { + return types.NewUCompact(big.NewInt(0)), fmt.Errorf("error getting UCompact field: %w", err) + } + + return ucompact, nil + }, + ) + if err != nil { + fmt.Printf("AppId parsing err: %s\n", err.Error()) + } else { + fmt.Println(id.Int64()) + } + + appKey, err := registry.ProcessDecodedFieldValue[types.Bytes]( + event.Fields, + func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 0 // Adjust this index if needed + }, + func(value any) (types.Bytes, error) { + fields, ok := value.(registry.DecodedFields) + if !ok { + return nil, fmt.Errorf("unexpected value type: %T", value) + } + + // The BoundedVec is likely represented as a slice of U8 + byteSlice, err := registry.GetDecodedFieldAsSliceOfType[types.U8](fields, func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 0 + }) + + if err != nil { + return nil, fmt.Errorf("error getting byte slice: %w", err) + } + + // Convert the slice of U8 to types.Bytes + bytes := make(types.Bytes, len(byteSlice)) + for i, b := range byteSlice { + bytes[i] = byte(b) + } + + return bytes, nil + }, + ) + + if err != nil { + fmt.Printf("AppKey parsing err: %s\n", err.Error()) + } else { + // Convert Bytes to string for printing + stringValue := string(appKey) + fmt.Printf("AppKey from event (as string): %s\n", stringValue) + + // If you also want to see the hex representation: + hexString := hex.EncodeToString(appKey) + fmt.Printf("AppKey from event (as hex): 0x%s\n", hexString) + } + + } + parseTransactionFee(event) + } +} + +func parseBalanceTransfer(events []*parser.Event) { + for _, event := range events { + if event.Name == "Balances.Transfer" { + from, err := registry.ProcessDecodedFieldValue[*types.AccountID]( + event.Fields, + func(fieldIndex int, field *registry.DecodedField) bool { + + return field.Name == "sp_core.crypto.AccountId32.from" + }, + func(value any) (*types.AccountID, error) { + fields, ok := value.(registry.DecodedFields) + + if !ok { + return nil, fmt.Errorf("unexpected value: %v", value) + } + + accByteSlice, err := registry.GetDecodedFieldAsSliceOfType[types.U8](fields, func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 0 + }) + + if err != nil { + return nil, err + } + + var accBytes []byte + + for _, accByte := range accByteSlice { + accBytes = append(accBytes, byte(accByte)) + } + + return types.NewAccountID(accBytes) + }, + ) + a := from.ToHexString() + + // // add, _ := types.NewAddressFromHexAccountID(a) + // fmt.Println(from) + fmt.Printf("from address read from event: %s \n", a) + + to, err := registry.ProcessDecodedFieldValue[*types.AccountID]( + event.Fields, + + func(fieldIndex int, field *registry.DecodedField) bool { + + return field.Name == "sp_core.crypto.AccountId32.to" + }, + func(value any) (*types.AccountID, error) { + fields, ok := value.(registry.DecodedFields) + + if !ok { + return nil, fmt.Errorf("unexpected value: %v", value) + } + + accByteSlice, err := registry.GetDecodedFieldAsSliceOfType[types.U8](fields, func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 0 + }) + + if err != nil { + return nil, err + } + + var accBytes []byte + + for _, accByte := range accByteSlice { + accBytes = append(accBytes, byte(accByte)) + } + + return types.NewAccountID(accBytes) + }, + ) + if err != nil { + fmt.Printf("TO parsing err: %s\n", err.Error()) + } + fmt.Printf("To address read from event: %s \n", to.ToHexString()) + amount, err := registry.GetDecodedFieldAsType[types.U128]( + event.Fields, + func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 2 + }, + ) + if err != nil { + fmt.Printf("Amount parsing err: %s\n", err.Error()) + } + fmt.Printf("Amount transferred : %s \n", convInt(amount.String())) + if err != nil { + fmt.Printf("Balances.Deposit.Who: %s\n", err.Error()) + } + } + parseTransactionFee(event) + } +} + +func parseBond(events []*parser.Event) { + for _, event := range events { + if event.Name == "Staking.Bonded" { + from, err := registry.ProcessDecodedFieldValue[*types.AccountID]( + event.Fields, + func(fieldIndex int, field *registry.DecodedField) bool { + + return field.Name == "sp_core.crypto.AccountId32.stash" + }, + func(value any) (*types.AccountID, error) { + fields, ok := value.(registry.DecodedFields) + + if !ok { + return nil, fmt.Errorf("unexpected value: %v", value) + } + + accByteSlice, err := registry.GetDecodedFieldAsSliceOfType[types.U8](fields, func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 0 + }) + + if err != nil { + return nil, err + } + + var accBytes []byte + + for _, accByte := range accByteSlice { + accBytes = append(accBytes, byte(accByte)) + } + + return types.NewAccountID(accBytes) + }, + ) + a := from.ToHexString() + + // // add, _ := types.NewAddressFromHexAccountID(a) + // fmt.Println(from) + fmt.Printf("stash address read from event: %s \n", a) + + amount, err := registry.GetDecodedFieldAsType[types.U128]( + event.Fields, + func(fieldIndex int, field *registry.DecodedField) bool { + return fieldIndex == 1 + }, + ) + if err != nil { + fmt.Printf("Amount parsing err: %s\n", err.Error()) + } + fmt.Printf("Amount staked : %s \n", convInt(amount.String())) + if err != nil { + fmt.Printf("Amount staked: %s\n", err.Error()) + } + } + parseTransactionFee(event) + } +} diff --git a/avail-go/src/sdk/helper.go b/avail-go/src/sdk/helper.go new file mode 100644 index 000000000..e8a550798 --- /dev/null +++ b/avail-go/src/sdk/helper.go @@ -0,0 +1,359 @@ +package sdk + +import ( + "crypto/rand" + "encoding/hex" + "encoding/json" + "fmt" + "log" + "math/big" + "strings" + + "avail-go-sdk/src/rpc" + + "github.com/centrifuge/go-substrate-rpc-client/v4/scale" + "github.com/centrifuge/go-substrate-rpc-client/v4/signature" + "github.com/centrifuge/go-substrate-rpc-client/v4/types" + "github.com/centrifuge/go-substrate-rpc-client/v4/types/codec" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/vedhavyas/go-subkey" + "golang.org/x/crypto/blake2b" +) + +func ConvertMultiAddress(receiver string) (types.MultiAddress, error) { + _, pubkeyBytes, _ := subkey.SS58Decode(receiver) + address := subkey.EncodeHex(pubkeyBytes) + + dest, err := types.NewMultiAddressFromHexAccountID(address) + if err != nil { + _ = fmt.Errorf("cannot create address from given hex:%w", err) + return types.MultiAddress{}, err + } + return dest, nil +} + +type BigInt struct { + *big.Int +} + +func ConvertToBondAmount(input int64) *big.Int { + // Create a new big.Int for the input + inputBig := big.NewInt(input) + + // Create a big.Int for 10^18 + multiplier := new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil) + + // Multiply input by 10^18 + result := new(big.Int).Mul(inputBig, multiplier) + + return result +} + +// UnmarshalJSON defines custom unmarshalling for BigInt. +func (bi *BigInt) UnmarshalJSON(data []byte) error { + // Unmarshal as raw JSON string + var rawString string + if err := json.Unmarshal(data, &rawString); err != nil { + return err + } + + // Remove 0x prefix if present + str := strings.TrimPrefix(rawString, "0x") + + // Initialize bi.Int if it's nil + if bi.Int == nil { + bi.Int = new(big.Int) + } + + // If the string is empty, set bi to zero + if str == "" { + bi.SetInt64(0) + return nil + } + + // Parse the string in base 16 + _, success := bi.SetString(str, 16) + if !success { + return fmt.Errorf("invalid hex string") + } + + return nil +} + +func FormatBN(n BigInt) string { + s := n.String() // Convert number to string + var result strings.Builder + count := 0 + + for i := len(s) - 1; i >= 0; i-- { + if count == 3 { + result.WriteString(",") + count = 0 + } + result.WriteByte(s[i]) + count++ + } + + // Reverse the string to get the correct order + reversed := result.String() + var formatted strings.Builder + for i := len(reversed) - 1; i >= 0; i-- { + formatted.WriteByte(reversed[i]) + } + + return formatted.String() +} +func RandToken(n int) (string, error) { + bytes := make([]byte, n) + if _, err := rand.Read(bytes); err != nil { + return "", err + } + return hex.EncodeToString(bytes), nil +} + +func KeyringFromSeed(seed string) (signature.KeyringPair, error) { + return signature.KeyringPairFromSecret(seed, 42) +} + +type Bytes []byte + +func NewBytes(b []byte) Bytes { + return Bytes(b) +} + +func EncodeToHex(hash interface{}) (string, error) { + return codec.EncodeToHex(hash) +} + +func convInt(val string) string { + bigIntValue := new(big.Int) + bigIntValue.SetString(val, 10) + + divisor := new(big.Int) + divisor.SetString("1000000000000000000", 10) + + bigFloatValue := new(big.Float).SetInt(bigIntValue) + divisorFloat := new(big.Float).SetInt(divisor) + result := new(big.Float).Quo(bigFloatValue, divisorFloat) + + x := (result.Text('f', 18)) + return x +} + +func CreateChannel() chan types.Hash { + c := make(chan types.Hash, 1) + return c +} + +func ExistentialDeposit(api *SubstrateAPI) { + + meta, err := api.RPC.State.GetMetadataLatest() + if err != nil { + panic(fmt.Sprintf("cannot get metadata: %v", err)) + } + var existentialDeposit types.U128 + + for _, mod := range meta.AsMetadataV14.Pallets { + if string(mod.Name) == "Balances" { + for _, constant := range mod.Constants { + if string(constant.Name) == "ExistentialDeposit" { + err = codec.Decode(constant.Value, &existentialDeposit) + if err != nil { + log.Fatalf("Failed to decode ExistentialDeposit: %v", err) + } + fmt.Printf("Existential Deposit: %d\n", existentialDeposit) + return + } + } + } + } + + fmt.Printf("Existential Deposit: %d\n", existentialDeposit) +} + +func NewCall(api *SubstrateAPI, callName string, args ...interface{}) (types.Call, error) { + meta, err := api.RPC.State.GetMetadataLatest() + if err != nil { + return types.Call{}, err + } + return types.NewCall(meta, callName, args...) +} + +func NewHashFromHexString(hexStr string) (types.Hash, error) { + return types.NewHashFromHexString(hexStr) +} + +func NewU256(value *big.Int) types.U256 { + return types.NewU256(*value) +} + +func NewMultiAddressFromHexAccountID(hexStr string) (types.MultiAddress, error) { + return types.NewMultiAddressFromHexAccountID(hexStr) +} + +func NewMultiAddressFromAccountID(accountID []byte) (types.MultiAddress, error) { + return types.NewMultiAddressFromAccountID(accountID) +} + +func NewU8(value uint8) types.U8 { + return types.U8(value) +} + +func NewU32(value uint32) types.U32 { + return types.U32(value) +} + +func NewU64(value uint64) types.U64 { + return types.U64(value) +} + +func NewU128(value *big.Int) types.U128 { + return types.NewU128(*value) +} + +func KeyringPairFromSecret(seed string, keyType uint16) (signature.KeyringPair, error) { + return signature.KeyringPairFromSecret(seed, keyType) +} + +type WaitFor int + +const ( + BlockInclusion WaitFor = iota + 1 + BlockFinalization +) + +func (w WaitFor) String() string { + return [...]string{"BlockInclusion", "BlockFinalization"}[w-1] +} + +// EnumIndex - Creating common behavior - give the type a EnumIndex function +func (w WaitFor) EnumIndex() int { + return int(w) +} + +type Payee uint8 + +const ( + Staked WaitFor = iota + Stash + Controller + Account + None +) + +func (w Payee) String() string { + return [...]string{"Staked", "Stash", "Controller", "Account", "None"}[w] +} + +// EnumIndex - Creating common behavior - give the type a EnumIndex function +func (w Payee) EnumIndex() uint8 { + return uint8(w) +} + +func GetData(hash types.Hash, api *SubstrateAPI, txHash types.Hash) error { + block, err := rpc.GetAvailBlock(hash, api.Client) + if err != nil { + return fmt.Errorf("cannot get block by hash:%w", err) + } + for _, ext := range block.Block.Extrinsics { + + // these values below are specific indexes only for data submission, differs with each extrinsic + if ext.IsSigned() && ext.Method.CallIndex.SectionIndex == 29 && ext.Method.CallIndex.MethodIndex == 1 { + enc, _ := EncodeToHex(ext) + cleanedHexString := strings.TrimPrefix(enc, "0x") + bytes, err := hex.DecodeString(cleanedHexString) + if err != nil { + log.Fatal(err) + } + hash := blake2b.Sum256(bytes) + txHashDecoded := hexutil.Encode(hash[:]) + if txHashDecoded == txHash.Hex() { + arg := ext.Method.Args + str := string(arg) + slice := str[1:] + + fmt.Println("Data retrieved:", slice) + } + } + } + return nil +} + +type DispatchFeeModifier struct { + WeightMaximumFee types.U128 + WeightFeeDivider types.U32 + WeightFeeMultiplier types.U32 +} + +// Encode implements scale.Encodeable +func (d DispatchFeeModifier) Encode(encoder scale.Encoder) error { + // Encode WeightMaximumFee + if err := encoder.PushByte(1); err != nil { // 1 for Some + return err + } + if err := encoder.Encode(d.WeightMaximumFee); err != nil { + return err + } + + // Encode WeightFeeDivider + if err := encoder.PushByte(1); err != nil { // 1 for Some + return err + } + if err := encoder.Encode(d.WeightFeeDivider); err != nil { + return err + } + + // Encode WeightFeeMultiplier + if err := encoder.PushByte(1); err != nil { // 1 for Some + return err + } + if err := encoder.Encode(d.WeightFeeMultiplier); err != nil { + return err + } + + return nil +} + +// Decode implements scale.Decodeable +func (d *DispatchFeeModifier) Decode(decoder scale.Decoder) error { + // Decode WeightMaximumFee + optionByte, err := decoder.ReadOneByte() + if err != nil { + return err + } + if optionByte == 1 { // Some + if err := decoder.Decode(&d.WeightMaximumFee); err != nil { + return err + } + } else if optionByte != 0 { // Not None (0) or Some (1) + return fmt.Errorf("invalid option byte for WeightMaximumFee: %d", optionByte) + } + + // Decode WeightFeeDivider + optionByte, err = decoder.ReadOneByte() + if err != nil { + return err + } + if optionByte == 1 { // Some + if err := decoder.Decode(&d.WeightFeeDivider); err != nil { + return err + } + } else if optionByte != 0 { // Not None (0) or Some (1) + return fmt.Errorf("invalid option byte for WeightFeeDivider: %d", optionByte) + } + + // Decode WeightFeeMultiplier + optionByte, err = decoder.ReadOneByte() + if err != nil { + return err + } + if optionByte == 1 { // Some + if err := decoder.Decode(&d.WeightFeeMultiplier); err != nil { + return err + } + } else if optionByte != 0 { // Not None (0) or Some (1) + return fmt.Errorf("invalid option byte for WeightFeeMultiplier: %d", optionByte) + } + + return nil +} diff --git a/avail-go/src/sdk/sdk.go b/avail-go/src/sdk/sdk.go new file mode 100644 index 000000000..165273063 --- /dev/null +++ b/avail-go/src/sdk/sdk.go @@ -0,0 +1,32 @@ +package sdk + +import ( + "github.com/centrifuge/go-substrate-rpc-client/v4/client" + "github.com/centrifuge/go-substrate-rpc-client/v4/rpc" +) + +type SubstrateAPI struct { + RPC *rpc.RPC + Client client.Client +} + +func NewSDK(url string) (*SubstrateAPI, error) { + cl, err := client.Connect(url) + if err != nil { + return nil, err + } + + newRPC, err := rpc.NewRPC(cl) + if err != nil { + return nil, err + } + + return &SubstrateAPI{ + RPC: newRPC, + Client: cl, + }, nil +} + +func (api *SubstrateAPI) Close() { + api.Client.Close() +} diff --git a/avail-go/src/sdk/tx/transactions.go b/avail-go/src/sdk/tx/transactions.go new file mode 100644 index 000000000..8367acaf5 --- /dev/null +++ b/avail-go/src/sdk/tx/transactions.go @@ -0,0 +1,470 @@ +package tx + +import ( + "avail-go-sdk/src/sdk" + "avail-go-sdk/src/sdk/types" + "errors" + "fmt" + "math/big" + "strconv" + + "github.com/vedhavyas/go-subkey/v2" +) + +type WaitFor int + +const ( + BlockInclusion WaitFor = iota + 1 + BlockFinalization +) + +func (w WaitFor) String() string { + return [...]string{"BlockInclusion", "BlockFinalization"}[w-1] +} + +// EnumIndex - Creating common behavior - give the type a EnumIndex function +func (w WaitFor) EnumIndex() int { + return int(w) +} + +// SubmitData submits data to the chain +func SubmitData(api *sdk.SubstrateAPI, seed string, AppID int, data string, WaitForInclusion sdk.WaitFor) (types.Hash, types.Hash, error) { + keyringPair, err := sdk.KeyringFromSeed(seed) + if err != nil { + panic(fmt.Sprintf("cannot create KeyPair:%v", err)) + } + BlockHashCh2 := make(chan types.Hash) + txHashCh2 := make(chan types.Hash) + go func() { + err := sdk.NewExtrinsicWatch(api, "DataAvailability.submit_data", keyringPair, BlockHashCh2, txHashCh2, AppID, WaitForInclusion, sdk.NewBytes([]byte("test data"))) + if err != nil { + fmt.Printf("cannot create extrinsic: %v", err) + close(BlockHashCh2) + close(txHashCh2) + return + } + fmt.Println("Data submitted successfully") + }() + blockHash := <-BlockHashCh2 + txHash := <-txHashCh2 + return blockHash, txHash, nil +} + +func CreateApplicationKey(api *sdk.SubstrateAPI, seed string, data string, WaitForInclusion sdk.WaitFor) (types.Hash, types.Hash, error) { + keyringPair, err := sdk.KeyringFromSeed(seed) + if err != nil { + panic(fmt.Sprintf("cannot create KeyPair:%v", err)) + } + BlockHashCh2 := make(chan types.Hash) + txHashCh2 := make(chan types.Hash) + go func() { + err := sdk.NewExtrinsicWatch(api, "DataAvailability.create_application_key", keyringPair, BlockHashCh2, txHashCh2, 0, WaitForInclusion, sdk.NewBytes([]byte("22222"))) + if err != nil { + fmt.Printf("cannot create extrinsic: %v", err) + close(BlockHashCh2) + close(txHashCh2) + return + } + fmt.Println("Data submitted successfully") + }() + blockHash := <-BlockHashCh2 + txHash := <-txHashCh2 + return blockHash, txHash, nil + +} + +func SetApplicationKey(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, oldKey string, newKey string) (types.Hash, types.Hash, error) { + keyringPair, err := sdk.KeyringFromSeed(seed) + if err != nil { + panic(fmt.Sprintf("cannot create KeyPair:%v", err)) + } + BlockHashCh2 := make(chan types.Hash) + txHashCh2 := make(chan types.Hash) + go func() { + call, err := sdk.NewCall(api, "DataAvailability.set_application_key", sdk.NewBytes([]byte(oldKey)), sdk.NewBytes([]byte(newKey))) + if err != nil { + fmt.Printf("cannot create extrinsic: %v", err) + } + err = sdk.NewExtrinsicWatch(api, "Sudo.sudo", keyringPair, BlockHashCh2, txHashCh2, 0, WaitForInclusion, call) + if err != nil { + fmt.Printf("cannot create extrinsic: %v", err) + close(BlockHashCh2) + close(txHashCh2) + return + } + fmt.Println("Transaction submitted successfully") + }() + blockHash := <-BlockHashCh2 + txHash := <-txHashCh2 + return blockHash, txHash, nil + +} + +func SetSubmitDataFeeModifier(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, modifier sdk.DispatchFeeModifier) (types.Hash, types.Hash, error) { + keyringPair, err := sdk.KeyringFromSeed(seed) + if err != nil { + panic(fmt.Sprintf("cannot create KeyPair:%v", err)) + } + BlockHashCh2 := make(chan types.Hash) + txHashCh2 := make(chan types.Hash) + + go func() { + call, err := sdk.NewCall(api, "DataAvailability.set_submit_data_fee_modifier", modifier) + if err != nil { + fmt.Printf("cannot create extrinsic: %v", err) + } + err = sdk.NewExtrinsicWatch(api, "Sudo.sudo", keyringPair, BlockHashCh2, txHashCh2, 0, WaitForInclusion, call) + if err != nil { + fmt.Printf("cannot create extrinsic: %v", err) + close(BlockHashCh2) + close(txHashCh2) + return + } + fmt.Println("Transaction submitted successfully") + }() + blockHash := <-BlockHashCh2 + txHash := <-txHashCh2 + return blockHash, txHash, nil + +} + +func SubmitBlockLength(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, rows uint32, cols uint32) (types.Hash, types.Hash, error) { + keyringPair, err := sdk.KeyringFromSeed(seed) + if err != nil { + panic(fmt.Sprintf("cannot create KeyPair:%v", err)) + } + BlockHashCh2 := make(chan types.Hash) + txHashCh2 := make(chan types.Hash) + + go func() { + call, err := sdk.NewCall(api, "DataAvailability.submit_block_length_proposal", sdk.NewU32(rows), sdk.NewU32(cols)) + if err != nil { + fmt.Printf("cannot create extrinsic: %v", err) + } + err = sdk.NewExtrinsicWatch(api, "Sudo.sudo", keyringPair, BlockHashCh2, txHashCh2, 0, WaitForInclusion, call) + if err != nil { + fmt.Printf("cannot create extrinsic: %v", err) + close(BlockHashCh2) + close(txHashCh2) + return + } + fmt.Println("Data submitted successfully") + }() + blockHash := <-BlockHashCh2 + txHash := <-txHashCh2 + return blockHash, txHash, nil + +} + +func Bond(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, amount int64, Payee sdk.Payee) (types.Hash, types.Hash, error) { + keyringPair, err := sdk.KeyringFromSeed(seed) + if err != nil { + panic(fmt.Sprintf("cannot create KeyPair:%v", err)) + } + bondAmount := sdk.ConvertToBondAmount(amount) + + newBondAmount := new(big.Int) + newBondAmount.Set(bondAmount) + + BlockHashCh2 := make(chan types.Hash) + txHashCh2 := make(chan types.Hash) + bondAmountUCompact := types.NewUCompact(newBondAmount) + go func() { + err = sdk.NewExtrinsicWatch(api, "Staking.bond", keyringPair, BlockHashCh2, txHashCh2, 0, WaitForInclusion, bondAmountUCompact, sdk.NewU8(Payee.EnumIndex())) + if err != nil { + fmt.Printf("cannot create extrinsic: %v", err) + close(BlockHashCh2) + close(txHashCh2) + return + } + fmt.Println("Data submitted successfully") + }() + blockHash := <-BlockHashCh2 + txHash := <-txHashCh2 + return blockHash, txHash, nil +} + +func BondExtra(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, amount int64) (types.Hash, types.Hash, error) { + keyringPair, err := sdk.KeyringFromSeed(seed) + if err != nil { + panic(fmt.Sprintf("cannot create KeyPair:%v", err)) + } + BlockHashCh2 := make(chan types.Hash) + txHashCh2 := make(chan types.Hash) + bondAmount := sdk.ConvertToBondAmount(amount) + + newBondAmount := new(big.Int) + newBondAmount.Set(bondAmount) + bondAmountUCompact := types.NewUCompact(newBondAmount) + + go func() { + err = sdk.NewExtrinsicWatch(api, "Staking.bond_extra", keyringPair, BlockHashCh2, txHashCh2, 0, WaitForInclusion, bondAmountUCompact) + if err != nil { + fmt.Printf("cannot create extrinsic: %v", err) + close(BlockHashCh2) + close(txHashCh2) + return + } + fmt.Println("Data submitted successfully") + }() + blockHash := <-BlockHashCh2 + txHash := <-txHashCh2 + return blockHash, txHash, nil +} + +func Chill(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor) (types.Hash, types.Hash, error) { + keyringPair, err := sdk.KeyringFromSeed(seed) + if err != nil { + panic(fmt.Sprintf("cannot create KeyPair:%v", err)) + } + BlockHashCh2 := make(chan types.Hash) + txHashCh2 := make(chan types.Hash) + + go func() { + err = sdk.NewExtrinsicWatch(api, "Staking.chill", keyringPair, BlockHashCh2, txHashCh2, 0, WaitForInclusion) + if err != nil { + fmt.Printf("cannot create extrinsic: %v", err) + close(BlockHashCh2) + close(txHashCh2) + return + } + fmt.Println("Data submitted successfully") + }() + blockHash := <-BlockHashCh2 + txHash := <-txHashCh2 + return blockHash, txHash, nil +} + +func ChillOther(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, stash string) (types.Hash, types.Hash, error) { + keyringPair, err := sdk.KeyringFromSeed(seed) + if err != nil { + panic(fmt.Sprintf("cannot create KeyPair:%v", err)) + } + fmt.Println(keyringPair) + _, pubkeyBytes, _ := subkey.SS58Decode(stash) + hexString := subkey.EncodeHex(pubkeyBytes) + + fmt.Println(hexString) + dest, err := sdk.NewMultiAddressFromHexAccountID(hexString) + if err != nil { + return types.Hash{}, types.Hash{}, err + } + fmt.Println(dest) + BlockHashCh2 := make(chan types.Hash) + txHashCh2 := make(chan types.Hash) + + go func() { + err = sdk.NewExtrinsicWatch(api, "Staking.chill_other", keyringPair, BlockHashCh2, txHashCh2, 0, WaitForInclusion, dest.AsID) + if err != nil { + fmt.Printf("cannot create extrinsic: %v", err) + close(BlockHashCh2) + close(txHashCh2) + return + } + fmt.Println("Data submitted successfully") + }() + blockHash := <-BlockHashCh2 + txHash := <-txHashCh2 + return blockHash, txHash, nil +} + +func Nominate(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, stash []string) (types.Hash, types.Hash, error) { + keyringPair, err := sdk.KeyringFromSeed(seed) + if err != nil { + panic(fmt.Sprintf("cannot create KeyPair:%v", err)) + } + var dest []types.MultiAddress + for _, v := range stash { + _, pubkeyBytes, _ := subkey.SS58Decode(v) + hexString := subkey.EncodeHex(pubkeyBytes) + d, err := sdk.NewMultiAddressFromHexAccountID(hexString) + if err != nil { + return types.Hash{}, types.Hash{}, err + } + dest = append(dest, d) + } + + BlockHashCh2 := make(chan types.Hash) + txHashCh2 := make(chan types.Hash) + + go func() { + err = sdk.NewExtrinsicWatch(api, "Staking.nominate", keyringPair, BlockHashCh2, txHashCh2, 0, WaitForInclusion, dest) + if err != nil { + fmt.Printf("cannot create extrinsic: %v", err) + close(BlockHashCh2) + close(txHashCh2) + return + } + fmt.Println("Data submitted successfully") + }() + blockHash := <-BlockHashCh2 + txHash := <-txHashCh2 + return blockHash, txHash, nil +} + +func Unbond(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, amount types.UCompact) (types.Hash, types.Hash, error) { + keyringPair, err := sdk.KeyringFromSeed(seed) + if err != nil { + panic(fmt.Sprintf("cannot create KeyPair:%v", err)) + } + BlockHashCh2 := make(chan types.Hash) + txHashCh2 := make(chan types.Hash) + + go func() { + err = sdk.NewExtrinsicWatch(api, "Staking.unbond", keyringPair, BlockHashCh2, txHashCh2, 0, WaitForInclusion, amount) + if err != nil { + fmt.Printf("cannot create extrinsic: %v", err) + close(BlockHashCh2) + close(txHashCh2) + return + } + fmt.Println("Data submitted successfully") + }() + blockHash := <-BlockHashCh2 + txHash := <-txHashCh2 + return blockHash, txHash, nil +} + +func Validate(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, commissionNum int) (types.Hash, types.Hash, error) { + keyringPair, err := sdk.KeyringFromSeed(seed) + if err != nil { + panic(fmt.Sprintf("cannot create KeyPair:%v", err)) + } + BlockHashCh2 := make(chan types.Hash) + txHashCh2 := make(chan types.Hash) + commissionStr, err := commissionNumberToPerbill(commissionNum) + if err != nil { + fmt.Errorf("failed to convert commission to Perbill: %v", err) + } + + commission, err := strconv.ParseUint(commissionStr, 10, 64) + if err != nil { + fmt.Errorf("failed to parse commission string: %v", err) + } + + // Define the ValidatorPrefs struct manually + type ValidatorPrefs struct { + Commission types.UCompact + Blocked bool + } + + // Create the validator preferences struct + prefs := ValidatorPrefs{ + Commission: types.NewUCompactFromUInt(commission), + Blocked: false, + } + go func() { + err = sdk.NewExtrinsicWatch(api, "Staking.validate", keyringPair, BlockHashCh2, txHashCh2, 0, WaitForInclusion, prefs) + if err != nil { + fmt.Printf("cannot create extrinsic: %v", err) + close(BlockHashCh2) + close(txHashCh2) + return + } + fmt.Println("Data submitted successfully") + }() + blockHash := <-BlockHashCh2 + txHash := <-txHashCh2 + return blockHash, txHash, nil +} + +func commissionNumberToPerbill(value int) (string, error) { + if value < 0 || value > 100 { + return "", errors.New("Commission is limited to the following range: 0 - 100. It cannot be less than 0 or more than 100.") + } + + commission := strconv.Itoa(value) + "0000000" + // For some reason 0 commission is not defined as "0" but as "1". + if commission == "00000000" { + commission = "1" + } + + return commission, nil +} + +func TransferKeepAlive(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, dest string, amount types.UCompact) (types.Hash, types.Hash, error) { + keyringPair, err := sdk.KeyringFromSeed(seed) + if err != nil { + panic(fmt.Sprintf("cannot create KeyPair:%v", err)) + } + _, pubkeyBytes, _ := subkey.SS58Decode(dest) + hexString := subkey.EncodeHex(pubkeyBytes) + destAddr, err := sdk.NewMultiAddressFromHexAccountID(hexString) + if err != nil { + return types.Hash{}, types.Hash{}, err + } + BlockHashCh2 := make(chan types.Hash) + txHashCh2 := make(chan types.Hash) + + go func() { + err = sdk.NewExtrinsicWatch(api, "Balances.transfer_keep_alive", keyringPair, BlockHashCh2, txHashCh2, 0, WaitForInclusion, destAddr, amount) + if err != nil { + fmt.Printf("cannot create extrinsic: %v", err) + close(BlockHashCh2) + close(txHashCh2) + return + } + fmt.Println("Transaction submitted successfully") + }() + blockHash := <-BlockHashCh2 + txHash := <-txHashCh2 + return blockHash, txHash, nil +} + +func TransferAllowDeath(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, dest string, amount types.UCompact) (types.Hash, types.Hash, error) { + keyringPair, err := sdk.KeyringFromSeed(seed) + if err != nil { + panic(fmt.Sprintf("cannot create KeyPair:%v", err)) + } + _, pubkeyBytes, _ := subkey.SS58Decode(dest) + hexString := subkey.EncodeHex(pubkeyBytes) + destAddr, err := sdk.NewMultiAddressFromHexAccountID(hexString) + if err != nil { + return types.Hash{}, types.Hash{}, err + } + BlockHashCh2 := make(chan types.Hash) + txHashCh2 := make(chan types.Hash) + + go func() { + err = sdk.NewExtrinsicWatch(api, "Balances.transfer_allow_death", keyringPair, BlockHashCh2, txHashCh2, 0, WaitForInclusion, destAddr, amount) + if err != nil { + fmt.Printf("cannot create extrinsic: %v", err) + close(BlockHashCh2) + close(txHashCh2) + return + } + fmt.Println("Transaction submitted successfully") + }() + blockHash := <-BlockHashCh2 + txHash := <-txHashCh2 + return blockHash, txHash, nil +} + +func TransferAll(api *sdk.SubstrateAPI, seed string, WaitForInclusion sdk.WaitFor, dest string) (types.Hash, types.Hash, error) { + keyringPair, err := sdk.KeyringFromSeed(seed) + if err != nil { + panic(fmt.Sprintf("cannot create KeyPair:%v", err)) + } + _, pubkeyBytes, _ := subkey.SS58Decode(dest) + hexString := subkey.EncodeHex(pubkeyBytes) + destAddr, err := sdk.NewMultiAddressFromHexAccountID(hexString) + if err != nil { + return types.Hash{}, types.Hash{}, err + } + BlockHashCh2 := make(chan types.Hash) + txHashCh2 := make(chan types.Hash) + + go func() { + err = sdk.NewExtrinsicWatch(api, "Balances.transfer_all", keyringPair, BlockHashCh2, txHashCh2, 0, WaitForInclusion, destAddr, sdk.NewU8(1)) //KeepAlive:yes, change to 0 for no + if err != nil { + fmt.Printf("cannot create extrinsic: %v", err) + close(BlockHashCh2) + close(txHashCh2) + return + } + fmt.Println("Transaction submitted successfully") + }() + blockHash := <-BlockHashCh2 + txHash := <-txHashCh2 + return blockHash, txHash, nil +} diff --git a/avail-go/src/sdk/types/types.go b/avail-go/src/sdk/types/types.go new file mode 100644 index 000000000..d96bfc3d2 --- /dev/null +++ b/avail-go/src/sdk/types/types.go @@ -0,0 +1,19 @@ +package types + +import ( + "github.com/centrifuge/go-substrate-rpc-client/v4/types" +) + +type MultiAddress = types.MultiAddress +type Call = types.Call +type Hash = types.Hash +type U32 = types.U32 +type U264 = types.U64 +type U256 = types.U256 +type U128 = types.U128 +type ChainProperties struct { + IsEthereum bool + SS58Format types.U32 + TokenDecimals types.U32 + TokenSymbol types.Text +} diff --git a/avail-go/src/sdk/types/ucompact.go b/avail-go/src/sdk/types/ucompact.go new file mode 100644 index 000000000..2e5e41512 --- /dev/null +++ b/avail-go/src/sdk/types/ucompact.go @@ -0,0 +1,54 @@ +package types + +import ( + "encoding/json" + "math/big" + + "github.com/centrifuge/go-substrate-rpc-client/v4/scale" +) + +type UCompact big.Int + +func NewUCompact(value *big.Int) UCompact { + return UCompact(*value) +} + +func (u *UCompact) Int64() int64 { + i := big.Int(*u) + return i.Int64() +} + +func NewUCompactFromUInt(value uint64) UCompact { + return NewUCompact(new(big.Int).SetUint64(value)) +} + +func (u *UCompact) Decode(decoder scale.Decoder) error { + ui, err := decoder.DecodeUintCompact() + if err != nil { + return err + } + + *u = UCompact(*ui) + return nil +} + +func (u UCompact) Encode(encoder scale.Encoder) error { + err := encoder.EncodeUintCompact(big.Int(u)) + if err != nil { + return err + } + return nil +} + +func (u UCompact) MarshalJSON() ([]byte, error) { + return json.Marshal(u.Int64()) +} + +func (u *UCompact) UnmarshalJSON(b []byte) error { + var i big.Int + if err := json.Unmarshal(b, &i); err != nil { + return err + } + *u = UCompact(i) + return nil +} diff --git a/examples/go/README.md b/examples/go/README.md deleted file mode 100644 index 57e862db1..000000000 --- a/examples/go/README.md +++ /dev/null @@ -1,17 +0,0 @@ -## Avail Go examples - -Sample examples written in Go that demonstrate interaction with the Avail network. For this examples Go `1.19` is needed. - -- Navigate to go examples folder. - - ```bash - cd examples/go - ``` -- Make sure that `config.json` is populated correctly. - -- To run particular example navigate to the folder with the example e.g. - - ```bash - cd connect - go run main.go --config config.json - ``` diff --git a/examples/go/dataSubmit/main.go b/examples/go/dataSubmit/main.go deleted file mode 100644 index 6ee9b4c15..000000000 --- a/examples/go/dataSubmit/main.go +++ /dev/null @@ -1,121 +0,0 @@ -package main - -import ( - "avail-gsrpc-examples/internal/config" - "avail-gsrpc-examples/internal/extrinsics" - "flag" - "fmt" - "log" - "os" - - gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" - "github.com/centrifuge/go-substrate-rpc-client/v4/signature" - "github.com/centrifuge/go-substrate-rpc-client/v4/types" -) - -// submitData creates a transaction and makes a Avail data submission -func submitData(size int, ApiURL string, Seed string, AppID int) error { - api, err := gsrpc.NewSubstrateAPI(ApiURL) - if err != nil { - return fmt.Errorf("cannot create api:%w", err) - } - - meta, err := api.RPC.State.GetMetadataLatest() - if err != nil { - return fmt.Errorf("cannot get metadata:%w", err) - } - - // Set data and appID according to need - data, _ := extrinsics.RandToken(size) - appID := 0 - - // if app id is greater than 0 then it must be created before submitting data - if AppID != 0 { - appID = AppID - } - - c, err := types.NewCall(meta, "DataAvailability.submit_data", types.NewBytes([]byte(data))) - if err != nil { - return fmt.Errorf("cannot create new call:%w", err) - } - - // Create the extrinsic - ext := types.NewExtrinsic(c) - - genesisHash, err := api.RPC.Chain.GetBlockHash(0) - if err != nil { - return fmt.Errorf("cannot get block hash:%w", err) - } - - rv, err := api.RPC.State.GetRuntimeVersionLatest() - if err != nil { - return fmt.Errorf("cannot get runtime version:%w", err) - } - - keyringPair, err := signature.KeyringPairFromSecret(Seed, 42) - if err != nil { - return fmt.Errorf("cannot create KeyPair:%w", err) - } - - key, err := types.CreateStorageKey(meta, "System", "Account", keyringPair.PublicKey) - if err != nil { - return fmt.Errorf("cannot create storage key:%w", err) - } - - var accountInfo types.AccountInfo - ok, err := api.RPC.State.GetStorageLatest(key, &accountInfo) - if err != nil || !ok { - return fmt.Errorf("cannot get latest storage:%w", err) - } - nonce := uint32(accountInfo.Nonce) - o := types.SignatureOptions{ - BlockHash: genesisHash, - Era: types.ExtrinsicEra{IsMortalEra: false}, - GenesisHash: genesisHash, - Nonce: types.NewUCompactFromUInt(uint64(nonce)), - SpecVersion: rv.SpecVersion, - Tip: types.NewUCompactFromUInt(0), - AppID: types.NewUCompactFromUInt(uint64(0)), - TransactionVersion: rv.TransactionVersion, - } - // Sign the transaction using Alice's default account - err = ext.Sign(keyringPair, o) - if err != nil { - return fmt.Errorf("cannot sign:%w", err) - } - - // Send the extrinsic - hash, err := api.RPC.Author.SubmitExtrinsic(ext) - if err != nil { - return fmt.Errorf("cannot submit extrinsic:%w", err) - } - fmt.Printf("Data submitted by Alice: %v against appID %v sent with hash %#x\n", data, appID, hash) - - return nil -} - -func main() { - var configJSON string - var config config.Config - flag.StringVar(&configJSON, "config", "", "config json file") - flag.Parse() - - if configJSON == "" { - log.Println("No config file provided. Exiting...") - os.Exit(0) - } - - err := config.GetConfig(configJSON) - if err != nil { - panic(fmt.Sprintf("cannot get config:%v", err)) - } - - size := 100 - if config.Size > 0 { - size = config.Size - } - err = submitData(size, config.ApiURL, config.Seed, config.AppID) - if err != nil { - panic(fmt.Sprintf("cannot submit data:%v", err)) - } -} diff --git a/examples/go/dataSubmitWatch/main.go b/examples/go/dataSubmitWatch/main.go deleted file mode 100644 index 97f656470..000000000 --- a/examples/go/dataSubmitWatch/main.go +++ /dev/null @@ -1,256 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "log" - "os" - "time" - - "avail-gsrpc-examples/internal/config" - "avail-gsrpc-examples/internal/extrinsics" - - gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" - "github.com/centrifuge/go-substrate-rpc-client/v4/registry" - . "github.com/centrifuge/go-substrate-rpc-client/v4/registry/retriever" - "github.com/centrifuge/go-substrate-rpc-client/v4/registry/state" - "github.com/centrifuge/go-substrate-rpc-client/v4/signature" - "github.com/centrifuge/go-substrate-rpc-client/v4/types" -) - -// The following example shows how submit data blob and track transaction status -func main() { - var configJSON string - var config config.Config - flag.StringVar(&configJSON, "config", "", "config json file") - flag.Parse() - - if configJSON == "" { - log.Println("No config file provided. Exiting...") - os.Exit(0) - } - - err := config.GetConfig(configJSON) - if err != nil { - panic(fmt.Sprintf("cannot get config:%v", err)) - } - - api, err := gsrpc.NewSubstrateAPI(config.ApiURL) - if err != nil { - panic(fmt.Sprintf("cannot create api:%v", err)) - } - - meta, err := api.RPC.State.GetMetadataLatest() - if err != nil { - panic(fmt.Sprintf("cannot get metadata:%v", err)) - } - - // Set data and appID according to need - size := 100 - if config.Size > 0 { - size = config.Size - } - - subData, _ := extrinsics.RandToken(size) - fmt.Println("Submitting data ...") - appID := 0 - - // if app id is greater than 0 then it must be created before submitting data - if config.AppID != 0 { - appID = config.AppID - } - - newCall, err := types.NewCall(meta, "DataAvailability.submit_data", types.NewBytes([]byte(subData))) - if err != nil { - panic(fmt.Sprintf("cannot create new call:%v", err)) - } - - // Create the extrinsic - ext := types.NewExtrinsic(newCall) - - genesisHash, err := api.RPC.Chain.GetBlockHash(0) - if err != nil { - panic(fmt.Sprintf("cannot get block hash:%v", err)) - } - - rv, err := api.RPC.State.GetRuntimeVersionLatest() - if err != nil { - panic(fmt.Sprintf("cannot get latest runtime version:%v", err)) - } - - keyringPair, err := signature.KeyringPairFromSecret(config.Seed, 42) - if err != nil { - panic(fmt.Sprintf("cannot create KeyPair:%v", err)) - } - - key, err := types.CreateStorageKey(meta, "System", "Account", keyringPair.PublicKey) - if err != nil { - panic(fmt.Sprintf("cannot create storage key:%w", err)) - } - - var accountInfo types.AccountInfo - ok, err := api.RPC.State.GetStorageLatest(key, &accountInfo) - if err != nil || !ok { - panic(fmt.Sprintf("cannot get latest storage:%v", err)) - } - - nonce := uint32(accountInfo.Nonce) - options := types.SignatureOptions{ - BlockHash: genesisHash, - Era: types.ExtrinsicEra{IsMortalEra: false}, - GenesisHash: genesisHash, - Nonce: types.NewUCompactFromUInt(uint64(nonce)), - SpecVersion: rv.SpecVersion, - Tip: types.NewUCompactFromUInt(100), - AppID: types.NewUCompactFromUInt(uint64(appID)), - TransactionVersion: rv.TransactionVersion, - } - - // Sign the transaction using Alice's default account - err = ext.Sign(keyringPair, options) - if err != nil { - panic(fmt.Sprintf("cannot sign:%v", err)) - } - - // Send the extrinsic - sub, err := api.RPC.Author.SubmitAndWatchExtrinsic(ext) - if err != nil { - panic(fmt.Sprintf("cannot submit extrinsic:%v", err)) - } - - fmt.Printf("Data submitted using APPID: %v \n", appID) - - defer sub.Unsubscribe() - timeout := time.After(100 * time.Second) - for { - select { - case status := <-sub.Chan(): - if status.IsInBlock { - fmt.Printf("Txn inside block %v\n", status.AsInBlock.Hex()) - } else if status.IsFinalized { - retriever, err := NewDefaultEventRetriever(state.NewEventProvider(api.RPC.State), api.RPC.State) - - if err != nil { - fmt.Printf("Couldn't create event retriever: %s", err) - } - h := status.AsFinalized - events, err := retriever.GetEvents(h) - - if err != nil { - log.Printf("Couldn't retrieve events") - } - for _, event := range events { - if event.Name == "DataAvailability.DataSubmitted" { - from, _ := registry.ProcessDecodedFieldValue[*types.AccountID]( - event.Fields, - func(fieldIndex int, field *registry.DecodedField) bool { - - return field.Name == "sp_core.crypto.AccountId32.who" - }, - func(value any) (*types.AccountID, error) { - fields, ok := value.(registry.DecodedFields) - - if !ok { - return nil, fmt.Errorf("unexpected value: %v", value) - } - - accByteSlice, err := registry.GetDecodedFieldAsSliceOfType[types.U8](fields, func(fieldIndex int, field *registry.DecodedField) bool { - return fieldIndex == 0 - }) - - if err != nil { - return nil, err - } - - var accBytes []byte - - for _, accByte := range accByteSlice { - accBytes = append(accBytes, byte(accByte)) - } - - return types.NewAccountID(accBytes) - }, - ) - a := from.ToHexString() - - // // add, _ := types.NewAddressFromHexAccountID(a) - // fmt.Println(from) - fmt.Printf("from address read from event: %s \n", a) - - dataHash, err := registry.ProcessDecodedFieldValue[*types.Hash]( - event.Fields, - func(fieldIndex int, field *registry.DecodedField) bool { - return fieldIndex == 1 - }, - func(value any) (*types.Hash, error) { - fields, ok := value.(registry.DecodedFields) - if !ok { - return nil, fmt.Errorf("unexpected value: %v", value) - } - - hashByteSlice, err := registry.GetDecodedFieldAsSliceOfType[types.U8](fields, func(fieldIndex int, field *registry.DecodedField) bool { - return fieldIndex == 0 - }) - - if err != nil { - return nil, err - } - - var hashBytes []byte - for _, hashByte := range hashByteSlice { - hashBytes = append(hashBytes, byte(hashByte)) - } - - hash := types.NewHash(hashBytes) - return &hash, nil - }, - ) - if err != nil { - fmt.Printf("DataHash parsing err: %s\n", err.Error()) - } else if dataHash == nil { - fmt.Println("DataHash is nil") - } else { - fmt.Printf("DataHash read from event: %s \n", dataHash.Hex()) - } - fmt.Printf("DataHash read from event: %s \n", dataHash.Hex()) - - } - - } - fmt.Printf("Txn inside finalized block\n") - hash := status.AsFinalized - err = getData(hash, api, subData) - if err != nil { - panic(fmt.Sprintf("cannot get data:%v", err)) - } - return - } - case <-timeout: - fmt.Printf("timeout of 100 seconds reached without getting finalized status for extrinsic") - return - } - } -} - -// getData extracts data from the block and compares it -func getData(hash types.Hash, api *gsrpc.SubstrateAPI, data string) error { - block, err := api.RPC.Chain.GetBlock(hash) - if err != nil { - return fmt.Errorf("cannot get block by hash:%w", err) - } - for _, ext := range block.Block.Extrinsics { - - // these values below are specific indexes only for data submission, differs with each extrinsic - if ext.Method.CallIndex.SectionIndex == 29 && ext.Method.CallIndex.MethodIndex == 1 { - arg := ext.Method.Args - str := string(arg) - slice := str[2:] - - fmt.Println("Data retrieved:", data) - if slice == data { - fmt.Println("Data found in block") - } - } - } - return nil -} diff --git a/examples/go/existential/main.go b/examples/go/existential/main.go deleted file mode 100644 index 9bec0cad0..000000000 --- a/examples/go/existential/main.go +++ /dev/null @@ -1,60 +0,0 @@ -package main - -import ( - "avail-gsrpc-examples/internal/config" - "flag" - "fmt" - "log" - "os" - - gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" - "github.com/centrifuge/go-substrate-rpc-client/v4/types" - . "github.com/centrifuge/go-substrate-rpc-client/v4/types/codec" -) - -func main() { - var configJSON string - var config config.Config - - flag.StringVar(&configJSON, "config", "", "config json file") - flag.Parse() - - if configJSON == "" { - log.Println("No config file provided. Exiting...") - os.Exit(0) - } - - err := config.GetConfig(configJSON) - if err != nil { - panic(fmt.Sprintf("cannot get config:%v", err)) - } - api, err := gsrpc.NewSubstrateAPI(config.ApiURL) - if err != nil { - panic(fmt.Sprintf("cannot create api client:%v", err)) - } - if api == nil || api.Client == nil { - log.Fatal("API client is not properly initialized") - } - meta, err := api.RPC.State.GetMetadataLatest() - if err != nil { - panic(fmt.Sprintf("cannot get metadata:%w", err)) - } - var existentialDeposit types.U128 - - for _, mod := range meta.AsMetadataV14.Pallets { - if string(mod.Name) == "Balances" { - for _, constant := range mod.Constants { - if string(constant.Name) == "ExistentialDeposit" { - err = Decode(constant.Value, &existentialDeposit) - if err != nil { - log.Fatalf("Failed to decode ExistentialDeposit: %v", err) - } - fmt.Printf("Existential Deposit: %d\n", existentialDeposit) - return - } - } - } - } - - fmt.Printf("Existential Deposit: %d\n", existentialDeposit) -} diff --git a/examples/go/internal/config/config.go b/examples/go/internal/config/config.go deleted file mode 100644 index e7c68434c..000000000 --- a/examples/go/internal/config/config.go +++ /dev/null @@ -1,38 +0,0 @@ -package config - -import ( - "encoding/json" - "io" - "os" -) - -type Config struct { - Seed string `json:"seed"` - ApiURL string `json:"api_url"` - Size int `json:"size"` - AppID int `json:"app_id"` - Dest string `json:"dest"` - Amount uint64 `json:"amount"` - SessionKeys string `json:"session_keys"` -} - -func (c *Config) GetConfig(configFileName string) error { - - jsonFile, err := os.Open(configFileName) - if err != nil { - return err - } - defer jsonFile.Close() - - byteValue, err := io.ReadAll(jsonFile) - if err != nil { - return err - } - - err = json.Unmarshal(byteValue, c) - if err != nil { - return err - } - - return nil -} diff --git a/examples/go/internal/extrinsics/extrinsics.go b/examples/go/internal/extrinsics/extrinsics.go deleted file mode 100644 index d2bb29876..000000000 --- a/examples/go/internal/extrinsics/extrinsics.go +++ /dev/null @@ -1,106 +0,0 @@ -package extrinsics - -import ( - "crypto/rand" - "encoding/hex" - "fmt" - "time" - - gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" - "github.com/centrifuge/go-substrate-rpc-client/v4/signature" - "github.com/centrifuge/go-substrate-rpc-client/v4/types" -) - -// SubmitData sends the extrinsic data to Substrate -func SubmitData(api *gsrpc.SubstrateAPI, data string, seed string, appID int, finalizedBlockCh chan types.Hash) error { - - meta, err := api.RPC.State.GetMetadataLatest() - if err != nil { - return err - } - - c, err := types.NewCall(meta, "DataAvailability.submit_data", types.NewBytes([]byte(data))) - if err != nil { - return fmt.Errorf("error creating new call: %s", err) - } - - // Create the extrinsic - ext := types.NewExtrinsic(c) - - genesisHash, err := api.RPC.Chain.GetBlockHash(0) - if err != nil { - return fmt.Errorf("error getting genesis hash: %s", err) - } - - rv, err := api.RPC.State.GetRuntimeVersionLatest() - if err != nil { - return fmt.Errorf("error retrieveing runtime version: %s", err) - } - - keyringPair, err := signature.KeyringPairFromSecret(seed, 42) - if err != nil { - return fmt.Errorf("error creating keyring pair: %s", err) - } - - key, err := types.CreateStorageKey(meta, "System", "Account", keyringPair.PublicKey) - if err != nil { - return fmt.Errorf("cannot create storage key:%w", err) - } - - var accountInfo types.AccountInfo - ok, err := api.RPC.State.GetStorageLatest(key, &accountInfo) - if err != nil || !ok { - return fmt.Errorf("cannot get latest storage:%v", err) - } - - nonce := uint32(accountInfo.Nonce) - options := types.SignatureOptions{ - BlockHash: genesisHash, - Era: types.ExtrinsicEra{IsMortalEra: false}, - GenesisHash: genesisHash, - Nonce: types.NewUCompactFromUInt(uint64(nonce)), - SpecVersion: rv.SpecVersion, - Tip: types.NewUCompactFromUInt(100), - AppID: types.NewUCompactFromUInt(uint64(appID)), - TransactionVersion: rv.TransactionVersion, - } - - // Sign the transaction using Alice's default account - err = ext.Sign(keyringPair, options) - if err != nil { - return fmt.Errorf("cannot sign:%v", err) - } - - // Send the extrinsic - sub, err := api.RPC.Author.SubmitAndWatchExtrinsic(ext) - if err != nil { - return fmt.Errorf("cannot submit extrinsic:%v", err) - } - - defer sub.Unsubscribe() - timeout := time.After(100 * time.Second) - for { - select { - case status := <-sub.Chan(): - if status.IsInBlock { - fmt.Printf("Txn inside block %v\n", status.AsInBlock.Hex()) - } else if status.IsFinalized { - fmt.Printf("Txn inside finalized block\n") - finalizedBlockCh <- status.AsFinalized - return nil - } - case <-timeout: - fmt.Printf("timeout of 100 seconds reached without getting finalized status for extrinsic") - return nil - } - } -} - -// RandToken generates a random hex value. -func RandToken(n int) (string, error) { - bytes := make([]byte, n) - if _, err := rand.Read(bytes); err != nil { - return "", err - } - return hex.EncodeToString(bytes), nil -} diff --git a/examples/go/nominated_staking/main.go b/examples/go/nominated_staking/main.go deleted file mode 100644 index 4435112e7..000000000 --- a/examples/go/nominated_staking/main.go +++ /dev/null @@ -1,130 +0,0 @@ -package main - -import ( - config "avail-gsrpc-examples/internal/config" - "flag" - "fmt" - "log" - "math/big" - "os" - - gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" - "github.com/centrifuge/go-substrate-rpc-client/v4/signature" - "github.com/centrifuge/go-substrate-rpc-client/v4/types" -) - -// submitData creates a transaction and makes a Avail data submission -func main() { - - var configJSON string - var config config.Config - flag.StringVar(&configJSON, "config", "", "config json file") - flag.Parse() - - if configJSON == "" { - log.Println("No config file provided. Exiting...") - os.Exit(0) - } - - err := config.GetConfig(configJSON) - if err != nil { - panic(fmt.Sprintf("cannot get config:%v", err)) - } - api, err := gsrpc.NewSubstrateAPI(config.ApiURL) - if err != nil { - fmt.Printf("cannot create api:%v", err) - } - keyringPair, err := signature.KeyringPairFromSecret(config.Seed, 42) - if err != nil { - fmt.Printf("cannot create LeyPair:%v", err) - } - - meta, err := api.RPC.State.GetMetadataLatest() - if err != nil { - fmt.Printf("cannot get metadata:%v", err) - } - - bondAmount := new(big.Int) - bondAmount.SetString("1000000000000000000000", 10) // Set bondAmount to 1000000000000000000000 - - // Convert big.Int to types.UCompact - bondAmountUCompact := types.NewUCompact(bondAmount) - - c, err := types.NewCall(meta, "Staking.bond", bondAmountUCompact, types.NewU8(0)) // "Staked" is usually represented as a constant in the API, here represented as 1 for demonstration - if err != nil { - fmt.Printf("cannot create new call:%v", err) - } - //Alice Stash Key - targetHex := "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f" - - // Convert the hex string to a MultiAddress - targetMultiAddress, err := types.NewMultiAddressFromHexAccountID(targetHex) - if err != nil { - fmt.Printf("Error converting hex string to MultiAddress: %v", err) - // handle the error appropriately - } - - //Uncomment following to use address - // _, pubkeyBytes, _ := subkey.SS58Decode("5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY") - // hexString := subkey.EncodeHex(pubkeyBytes) - - // dest, err := types.NewMultiAddressFromHexAccountID(hexString) - // if err != nil { - // fmt.Printf("cannot create new call:%v", err) - // } - d, err := types.NewCall(meta, "Staking.nominate", []types.MultiAddress{targetMultiAddress}) - if err != nil { - fmt.Printf("cannot create new call:%v", err) - } - - batchCall, err := types.NewCall(meta, "Utility.batch_all", []types.Call{c, d}) - if err != nil { - panic(err) - } - ext := types.NewExtrinsic(batchCall) - genesisHash, err := api.RPC.Chain.GetBlockHash(0) - if err != nil { - fmt.Printf("cannot get block hash:%v", err) - } - - rv, err := api.RPC.State.GetRuntimeVersionLatest() - if err != nil { - fmt.Printf("cannot get runtime version:%v", err) - } - - key, err := types.CreateStorageKey(meta, "System", "Account", keyringPair.PublicKey) - if err != nil { - fmt.Printf("cannot create storage key:%v", err) - } - - var accountInfo types.AccountInfo - ok, err := api.RPC.State.GetStorageLatest(key, &accountInfo) - if err != nil || !ok { - fmt.Printf("cannot get latest storage:%v", err) - } - - o := types.SignatureOptions{ - BlockHash: genesisHash, - Era: types.ExtrinsicEra{IsMortalEra: false}, - GenesisHash: genesisHash, - Nonce: types.NewUCompactFromUInt(uint64(accountInfo.Nonce)), - SpecVersion: rv.SpecVersion, - Tip: types.NewUCompactFromUInt(0), - AppID: types.NewUCompactFromUInt(uint64(0)), - TransactionVersion: rv.TransactionVersion, - } - - // Sign the transaction using Alice's default account - err = ext.Sign(keyringPair, o) - if err != nil { - fmt.Printf("cannot sign:%v", err) - } - // Send the extrinsic - hash, err := api.RPC.Author.SubmitExtrinsic(ext) - if err != nil { - fmt.Printf("cannot submit extrinsic:%v", err) - } - - fmt.Println(hash) - -} diff --git a/examples/go/properties/main.go b/examples/go/properties/main.go deleted file mode 100644 index 0bba38a3e..000000000 --- a/examples/go/properties/main.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "avail-gsrpc-examples/internal/config" - "flag" - "fmt" - "log" - "os" - - gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" - "github.com/centrifuge/go-substrate-rpc-client/v4/types" -) - -func main() { - var configJSON string - var config config.Config - - flag.StringVar(&configJSON, "config", "", "config json file") - flag.Parse() - - if configJSON == "" { - log.Println("No config file provided. Exiting...") - os.Exit(0) - } - - err := config.GetConfig(configJSON) - if err != nil { - panic(fmt.Sprintf("cannot get config:%v", err)) - } - api, err := gsrpc.NewSubstrateAPI(config.ApiURL) - if err != nil { - panic(fmt.Sprintf("cannot create api client:%v", err)) - } - if api == nil || api.Client == nil { - log.Fatal("API client is not properly initialized") - } - - var properties types.ChainProperties - err = api.Client.Call(&properties, "system_properties", nil) - - if err != nil { - panic(fmt.Sprintf("cannot get properties:%w", err)) - } - - fmt.Printf("Chain properties: \nIsEthereum:%#v\nTokenSymbol:%#v\nTokenDecimals:%d\n", properties.IsEthereum, properties.TokenSymbol, properties.TokenDecimals) - -} diff --git a/examples/go/queryProofData/main.go b/examples/go/queryProofData/main.go deleted file mode 100644 index 43b8934d5..000000000 --- a/examples/go/queryProofData/main.go +++ /dev/null @@ -1,134 +0,0 @@ -package main - -import ( - "avail-gsrpc-examples/internal/config" - "avail-gsrpc-examples/internal/extrinsics" - "flag" - "fmt" - "log" - "os" - - gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" - "github.com/centrifuge/go-substrate-rpc-client/v4/types" -) - -// ProofResponse struct represents the response from the queryDataProof2 RPC call -type ProofResponse struct { - DataProof DataProof - Message Message // Interface to capture different message types -} - -type TxDataRoot struct { - DataRoot types.Hash - BlobRoot types.Hash - BridgeRoot types.Hash -} - -// DataProof struct represents the data proof response -type DataProof struct { - Roots TxDataRoot - Proof []types.Hash - NumberOfLeaves uint32 // Change to uint32 to match Rust u32 - LeafIndex uint32 // Change to uint32 to match Rust u32 - Leaf types.Hash -} - -// Message interface represents the enum variants -type Message interface { - isMessage() -} - -type BoundedData struct { - Data []byte -} - -// BoundedDataMaxLen is the maximum length for the bounded data -const BoundedDataMaxLen = 32 // Adjust the maximum length as needed - -// ArbitraryMessage struct represents the ArbitraryMessage variant -type ArbitraryMessage struct { - BoundedData -} - -func (a *ArbitraryMessage) isMessage() {} - -// FungibleToken struct represents the FungibleToken variant -type FungibleToken struct { - AssetID types.Hash - Amount uint128 // Define uint128 type as needed -} - -func (f *FungibleToken) isMessage() {} - -// Define uint128 type if not already defined -type uint128 uint64 - -func main() { - var configJSON string - var config config.Config - - flag.StringVar(&configJSON, "config", "", "config json file") - flag.Parse() - - if configJSON == "" { - log.Println("No config file provided. Exiting...") - os.Exit(0) - } - - err := config.GetConfig(configJSON) - if err != nil { - panic(fmt.Sprintf("cannot get config:%v", err)) - } - - api, err := gsrpc.NewSubstrateAPI(config.ApiURL) - if err != nil { - panic(fmt.Sprintf("cannot create api client:%v", err)) - } - - var finalizedBlockCh = make(chan types.Hash) - go func() { - err = extrinsics.SubmitData(api, "data", config.Seed, 1, finalizedBlockCh) - if err != nil { - panic(fmt.Sprintf("cannot submit data:%v", err)) - } - }() - - // block hash to query proof - blockHash := <-finalizedBlockCh - fmt.Printf("Transaction included in finalized block: %v\n", blockHash.Hex()) - h, _ := types.NewHashFromHexString(blockHash.Hex()) - transactionIndex := types.NewU32(1) - - // query proof - var response ProofResponse - err = api.Client.Call(&response, "kate_queryDataProof", transactionIndex, h) - if err != nil { - panic(fmt.Sprintf("%v\n", err)) - } - fmt.Printf("DataRoot:%v\n", response.DataProof.Roots.DataRoot.Hex()) - fmt.Printf("BlobRoot:%v\n", response.DataProof.Roots.BlobRoot.Hex()) - fmt.Printf("BridgeRoot:%v\n", response.DataProof.Roots.BridgeRoot.Hex()) - // print array of proof - fmt.Printf("Proof:\n") - for _, p := range response.DataProof.Proof { - fmt.Printf("%v\n", p.Hex()) - } - - fmt.Printf("Number of leaves: %v\n", response.DataProof.NumberOfLeaves) - fmt.Printf("Leaf index: %v\n", response.DataProof.LeafIndex) - fmt.Printf("Leaf: %v\n", response.DataProof.Leaf.Hex()) - - // Access the message based on its type - if response.Message != nil { - switch m := response.Message.(type) { - case *ArbitraryMessage: - fmt.Println("Arbitrary Message:", m.BoundedData) - case *FungibleToken: - fmt.Println("Fungible Token AssetID:", m.AssetID.Hex(), "Amount:", m.Amount) - default: - fmt.Println("Unknown Message Type") - } - } else { - fmt.Println("Message: null") - } -} diff --git a/examples/go/query_Rows/main.go b/examples/go/query_Rows/main.go deleted file mode 100644 index 631650ae7..000000000 --- a/examples/go/query_Rows/main.go +++ /dev/null @@ -1,149 +0,0 @@ -package main - -import ( - "avail-gsrpc-examples/internal/config" - "avail-gsrpc-examples/internal/extrinsics" - "encoding/json" - "flag" - "fmt" - "log" - "math/big" - "os" - "strings" - - gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" - "github.com/centrifuge/go-substrate-rpc-client/v4/types" -) - -// type x [][]types.U256 -type BigInt struct { - *big.Int -} - -// UnmarshalJSON defines custom unmarshalling for BigInt. -func (bi *BigInt) UnmarshalJSON(data []byte) error { - // Unmarshal as raw JSON string - var rawString string - if err := json.Unmarshal(data, &rawString); err != nil { - return err - } - - // Remove 0x prefix if present - str := strings.TrimPrefix(rawString, "0x") - - // Initialize bi.Int if it's nil - if bi.Int == nil { - bi.Int = new(big.Int) - } - - // If the string is empty, set bi to zero - if str == "" { - bi.SetInt64(0) - return nil - } - - // Parse the string in base 16 - _, success := bi.SetString(str, 16) - if !success { - return fmt.Errorf("invalid hex string") - } - - return nil -} - -func main() { - var configJSON string - var config config.Config - - flag.StringVar(&configJSON, "config", "", "config json file") - flag.Parse() - - if configJSON == "" { - log.Println("No config file provided. Exiting...") - os.Exit(0) - } - - err := config.GetConfig(configJSON) - if err != nil { - panic(fmt.Sprintf("cannot get config:%v", err)) - } - api, err := gsrpc.NewSubstrateAPI(config.ApiURL) - if err != nil { - panic(fmt.Sprintf("cannot create api client:%v", err)) - } - if api == nil || api.Client == nil { - log.Fatal("API client is not properly initialized") - } - - var finalizedBlockCh = make(chan types.Hash) - go func() { - err = extrinsics.SubmitData(api, "data", config.Seed, 1, finalizedBlockCh) - if err != nil { - panic(fmt.Sprintf("cannot submit data:%v", err)) - } - }() - - // block hash to query proof - blockHash := <-finalizedBlockCh - fmt.Printf("Transaction included in finalized block: %v\n", blockHash.Hex()) - h, err := types.NewHashFromHexString(blockHash.Hex()) - if err != nil { - panic(fmt.Sprintf("cannot create api client:%v", err)) - } - // appId := types.NewUCompactFromUInt(uint64(1)) - - response := make([][]BigInt, 1) - response[0] = make([]BigInt, 1) - - // Assuming types.U256 has a constructor like NewU256 - zeroValue := types.NewU256(*big.NewInt(0)) // Replace with the actual constructor or method - response[0][0] = BigInt{zeroValue.Int} - - defer func() { - if r := recover(); r != nil { - fmt.Println("Recovered in main", r) - } - }() - myArr := make([]uint32, 1) - myArr[0] = 0 - err = api.Client.Call(&response, "kate_queryRows", myArr, h) - if err != nil { - fmt.Println("Error calling api.Client.Call:", err) - return - } - - formattedResponse := make([][]string, len(response)) - - for i, innerSlice := range response { - formattedResponse[i] = make([]string, len(innerSlice)) - for j, num := range innerSlice { - formattedResponse[i][j] = formatBigInt(num) - } - } - fmt.Println(formattedResponse) - -} - -func formatBigInt(n BigInt) string { - s := n.String() // Convert number to string - var result strings.Builder - count := 0 - - for i := len(s) - 1; i >= 0; i-- { - if count == 3 { - result.WriteString(",") - count = 0 - } - result.WriteByte(s[i]) - count++ - } - - // Reverse the string to get the correct order - reversed := result.String() - var formatted strings.Builder - for i := len(reversed) - 1; i >= 0; i-- { - formatted.WriteByte(reversed[i]) - } - - return formatted.String() -} diff --git a/examples/go/record_event/main.go b/examples/go/record_event/main.go deleted file mode 100644 index 264c070c8..000000000 --- a/examples/go/record_event/main.go +++ /dev/null @@ -1,227 +0,0 @@ -package main - -import ( - "fmt" - "log" - "math/big" - - gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" - "github.com/centrifuge/go-substrate-rpc-client/v4/registry" - . "github.com/centrifuge/go-substrate-rpc-client/v4/registry/retriever" - "github.com/centrifuge/go-substrate-rpc-client/v4/registry/state" - "github.com/centrifuge/go-substrate-rpc-client/v4/types" -) - -func main() { - - api, err := gsrpc.NewSubstrateAPI("wss://turing-rpc.avail.so/ws") - if err != nil { - panic(fmt.Sprintf("cannot create api client:%v", err)) - } - retriever, err := NewDefaultEventRetriever(state.NewEventProvider(api.RPC.State), api.RPC.State) - - if err != nil { - fmt.Printf("Couldn't create event retriever: %s", err) - return - } - // meta, err := api.RPC.State.GetMetadataLatest() - // if err != nil { - // fmt.Errorf("cannot get metadata:%w", err) - // } - // fmt.Println(meta) - // prop, _ := api.RPC.System.Properties() - // fmt.Println(prop) - - h, _ := types.NewHashFromHexString("0x31c31825a80ac53544208e6135a583a5dfa1aef7bf50f79b363d82d3b6b3392a") - // key, err := types.CreateStorageKey(meta, "System", "Events", nil, nil) - // if err != nil { - // log.Fatalf("Failed to create storage key: %v", err) - // } - // rawEvents, err := api.RPC.State.GetStorageRaw(key, h) - // if err != nil { - // log.Fatalf("Failed to fetch events: %v", err) - // } - // events := types.EventRecords{} - // err = types.EventRecordsRaw(*rawEvents).DecodeEventRecords(meta, &events) - // if err != nil { - // log.Fatalf("Failed to decode events: %v", err) - // } - // // fmt.Println(events) - - // if rawEvents != nil && len(*rawEvents) > 0 { - // err = types.EventRecordsRaw(*rawEvents).DecodeEventRecords(meta, &events) - // if err != nil { - // log.Fatalf("Failed to decode events: %v", err) - // } - - // for _, e := range events.Utility_ItemCompleted { - - // fmt.Printf("Transfer event: %v\n", e) - - // } - - // } else { - // fmt.Println("No events found in the block") - // } - events, err := retriever.GetEvents(h) - - if err != nil { - log.Printf("Couldn't retrieve events") - return - } - - // log.Printf("Found %d events for '%s', at block number %d.\n", len(events), testURL, header.Number) - - // Example of the events returned structure - for _, event := range events { - if event.Name == "Balances.Transfer" { - // log.Printf("Event ID: %x \n", event.EventID) - // log.Printf("Event Name: %s \n", event.Name) - // log.Printf("Event Fields Count: %d \n", len(event.Fields)) - // for k, v := range event.Fields { - // log.Printf("Field Name: %d \n", k) - // log.Printf("Field Type: %v \n", reflect.TypeOf(v)) - // log.Printf("Field Value: %v \n", v) - // log.Println("testing registery events") - // if k != 2 { - // w, err := registry.GetDecodedFieldAsType[registry.DecodedFields]( - // event.Fields, - // func(fieldIndex int, field *registry.DecodedField) bool { - // return fieldIndex == k - // }, - // ) - // if err != nil { - // fmt.Printf("Balances.Deposit.Who: %s\n", err.Error()) - // } - // fmt.Println(w) - // } else { - // w, err := registry.GetDecodedFieldAsType[types.U128]( - // event.Fields, - // func(fieldIndex int, field *registry.DecodedField) bool { - // return fieldIndex == k - // }, - // ) - // if err != nil { - // fmt.Printf("Balances.Deposit.Who: %s\n", err.Error()) - // } - // fmt.Println(w) - // } - // } - from, err := registry.ProcessDecodedFieldValue[*types.AccountID]( - event.Fields, - func(fieldIndex int, field *registry.DecodedField) bool { - - return field.Name == "sp_core.crypto.AccountId32.from" - }, - func(value any) (*types.AccountID, error) { - fields, ok := value.(registry.DecodedFields) - - if !ok { - return nil, fmt.Errorf("unexpected value: %v", value) - } - - accByteSlice, err := registry.GetDecodedFieldAsSliceOfType[types.U8](fields, func(fieldIndex int, field *registry.DecodedField) bool { - return fieldIndex == 0 - }) - - if err != nil { - return nil, err - } - - var accBytes []byte - - for _, accByte := range accByteSlice { - accBytes = append(accBytes, byte(accByte)) - } - - return types.NewAccountID(accBytes) - }, - ) - a := from.ToHexString() - - // // add, _ := types.NewAddressFromHexAccountID(a) - // fmt.Println(from) - fmt.Println(a) - - to, err := registry.ProcessDecodedFieldValue[*types.AccountID]( - event.Fields, - - func(fieldIndex int, field *registry.DecodedField) bool { - - return field.Name == "sp_core.crypto.AccountId32.to" - }, - func(value any) (*types.AccountID, error) { - fields, ok := value.(registry.DecodedFields) - - if !ok { - return nil, fmt.Errorf("unexpected value: %v", value) - } - - accByteSlice, err := registry.GetDecodedFieldAsSliceOfType[types.U8](fields, func(fieldIndex int, field *registry.DecodedField) bool { - return fieldIndex == 0 - }) - - if err != nil { - return nil, err - } - - var accBytes []byte - - for _, accByte := range accByteSlice { - accBytes = append(accBytes, byte(accByte)) - } - - return types.NewAccountID(accBytes) - }, - ) - if err != nil { - fmt.Printf("TO parsing err: %s\n", err.Error()) - } - fmt.Println(to.ToHexString()) - amount, err := registry.GetDecodedFieldAsType[types.U128]( - event.Fields, - func(fieldIndex int, field *registry.DecodedField) bool { - return fieldIndex == 2 - }, - ) - if err != nil { - fmt.Printf("Amount parsing err: %s\n", err.Error()) - } - fmt.Printf("Amount: %s \n", convInt(amount.String())) - if err != nil { - fmt.Printf("Balances.Deposit.Who: %s\n", err.Error()) - } - } - if event.Name == "TransactionPayment.TransactionFeePaid" { - - amount, err := registry.GetDecodedFieldAsType[types.U128]( - event.Fields, - func(fieldIndex int, field *registry.DecodedField) bool { - return fieldIndex == 1 - }, - ) - if err != nil { - fmt.Printf("Amount parsing err: %s\n", err.Error()) - } - fmt.Printf("Amount: %s \n", convInt(amount.String())) - if err != nil { - fmt.Printf("Balances.Deposit.Who: %s\n", err.Error()) - } - } - } -} - -func convInt(val string) string { - bigIntValue := new(big.Int) - bigIntValue.SetString(val, 10) - - divisor := new(big.Int) - divisor.SetString("1000000000000000000", 10) - - bigFloatValue := new(big.Float).SetInt(bigIntValue) - divisorFloat := new(big.Float).SetInt(divisor) - result := new(big.Float).Quo(bigFloatValue, divisorFloat) - - x := (result.Text('f', 18)) - return x -} diff --git a/examples/go/staking_validate/main.go b/examples/go/staking_validate/main.go deleted file mode 100644 index 455b700c4..000000000 --- a/examples/go/staking_validate/main.go +++ /dev/null @@ -1,166 +0,0 @@ -package main - -import ( - config "avail-gsrpc-examples/internal/config" - "flag" - "fmt" - "log" - "math/big" - "os" - - gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" - "github.com/centrifuge/go-substrate-rpc-client/v4/signature" - "github.com/centrifuge/go-substrate-rpc-client/v4/types" -) - -// submitData creates a transaction and makes a Avail data submission -func main() { - - var configJSON string - var config config.Config - flag.StringVar(&configJSON, "config", "", "config json file") - flag.Parse() - - if configJSON == "" { - log.Println("No config file provided. Exiting...") - os.Exit(0) - } - - err := config.GetConfig(configJSON) - if err != nil { - panic(fmt.Sprintf("cannot get config:%v", err)) - } - api, err := gsrpc.NewSubstrateAPI(config.ApiURL) - if err != nil { - fmt.Printf("cannot create api:%v", err) - } - keyringPair, err := signature.KeyringPairFromSecret(config.Seed, 42) - if err != nil { - fmt.Printf("cannot create LeyPair:%v", err) - } - - meta, err := api.RPC.State.GetMetadataLatest() - if err != nil { - fmt.Printf("cannot get metadata:%v", err) - } - // sessionKeys := config.SessionKeys - // keys := deconstructSessionKeys(sessionKeys) - bondAmount := new(big.Int) - bondAmount.SetString("1000000000000000000000", 10) // Set bondAmount to 1000000000000000000000 - - // Convert big.Int to types.UCompact - bondAmountUCompact := types.NewUCompact(bondAmount) - - c, err := types.NewCall(meta, "Staking.bond", bondAmountUCompact, types.NewU8(0)) // "Staked" is usually represented as a constant in the API, here represented as 1 for demonstration - if err != nil { - fmt.Printf("cannot create new call:%v", err) - } - // Create the extrinsic - ext := types.NewExtrinsic(c) - // proof := types.NewU8(0) - - // d, err := types.NewCall(meta, "Session.set_keys", keys, proof) // "Staked" is usually represented as a constant in the API, here represented as 1 for demonstration - // if err != nil { - // fmt.Printf("cannot create new call:%v", err) - // } - // // Create the extrinsic - // ext := types.NewExtrinsic(d) - - // batchCall, err := types.NewCall(meta, "Utility.batch_all", []types.Call{c, d}) - // if err != nil { - // panic(err) - // } - genesisHash, err := api.RPC.Chain.GetBlockHash(0) - if err != nil { - fmt.Printf("cannot get block hash:%v", err) - } - - rv, err := api.RPC.State.GetRuntimeVersionLatest() - if err != nil { - fmt.Printf("cannot get runtime version:%v", err) - } - - key, err := types.CreateStorageKey(meta, "System", "Account", keyringPair.PublicKey) - if err != nil { - fmt.Printf("cannot create storage key:%v", err) - } - - var accountInfo types.AccountInfo - ok, err := api.RPC.State.GetStorageLatest(key, &accountInfo) - if err != nil || !ok { - fmt.Printf("cannot get latest storage:%v", err) - } - - nonce := uint32(accountInfo.Nonce) - o := types.SignatureOptions{ - BlockHash: genesisHash, - Era: types.ExtrinsicEra{IsMortalEra: false}, - GenesisHash: genesisHash, - Nonce: types.NewUCompactFromUInt(uint64(nonce)), - SpecVersion: rv.SpecVersion, - Tip: types.NewUCompactFromUInt(0), - AppID: types.NewUCompactFromUInt(uint64(0)), - TransactionVersion: rv.TransactionVersion, - } - - // Sign the transaction using Alice's default account - err = ext.Sign(keyringPair, o) - if err != nil { - fmt.Printf("cannot sign:%v", err) - } - // Send the extrinsic - hash, err := api.RPC.Author.SubmitExtrinsic(ext) - if err != nil { - fmt.Printf("cannot submit extrinsic:%v", err) - } - - fmt.Println(hash) - -} - -type SessionKeys struct { - Babe string - Grandpa string - ImOnline string - AuthorityDiscovery string -} - -type ValidatorPreference struct { - Commission string - Block bool -} - -func defineValidatorPreference() ValidatorPreference { - // "5" means 5 percent. - commission := "5" + "0000000" - - // For some reason 0 commission is not defined as "0" but as "1". - if commission == "00000000" { - commission = "1" - } - - // Returning the ValidatorPreference struct. - return ValidatorPreference{ - Commission: commission, - Block: false, - } -} - -func deconstructSessionKeys(sessionKeys string) SessionKeys { - // Removing the "0x" prefix from the session keys - keys := sessionKeys[2:] - - // Splitting the keys into four parts - babeKey := "0x" + keys[0:64] - grandpaKey := "0x" + keys[64:128] - imonlineKey := "0x" + keys[128:192] - authorityDiscoveryKey := "0x" + keys[192:256] - - // Returning the keys as a SessionKeys struct - return SessionKeys{ - Babe: babeKey, - Grandpa: grandpaKey, - ImOnline: imonlineKey, - AuthorityDiscovery: authorityDiscoveryKey, - } -} diff --git a/examples/go/transfer_basic/main.go b/examples/go/transfer_basic/main.go deleted file mode 100644 index ed5769b31..000000000 --- a/examples/go/transfer_basic/main.go +++ /dev/null @@ -1,141 +0,0 @@ -package main - -import ( - "avail-gsrpc-examples/internal/config" - "flag" - "fmt" - "log" - "math" - "os" - "time" - - gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" - "github.com/centrifuge/go-substrate-rpc-client/v4/signature" - "github.com/centrifuge/go-substrate-rpc-client/v4/types" - "github.com/vedhavyas/go-subkey/v2" -) - -func transfer(api *gsrpc.SubstrateAPI, senderSeed string, receiver string, amount uint64) error { - - meta, err := api.RPC.State.GetMetadataLatest() - if err != nil { - return fmt.Errorf("cannot get metadata:%w", err) - } - - _, pubkeyBytes, _ := subkey.SS58Decode(receiver) - hexString := subkey.EncodeHex(pubkeyBytes) - - dest, err := types.NewMultiAddressFromHexAccountID(hexString) - if err != nil { - return fmt.Errorf("cannot create address from given hex:%w", err) - } - - balanceCall, err := types.NewCall(meta, "Balances.transfer_keep_alive", dest, types.NewUCompactFromUInt(amount)) - if err != nil { - return fmt.Errorf("cannot create balance call:%w", err) - } - - // Create the extrinsic - ext := types.NewExtrinsic(balanceCall) - - genesisHash, err := api.RPC.Chain.GetBlockHash(0) - if err != nil { - return fmt.Errorf("cannot get block hash:%w", err) - } - - rv, err := api.RPC.State.GetRuntimeVersionLatest() - if err != nil { - return fmt.Errorf("cannot runtime version:%w", err) - } - - keyringPair, err := signature.KeyringPairFromSecret(senderSeed, 42) - if err != nil { - return fmt.Errorf("cannot create KeyPair:%w", err) - } - - key, err := types.CreateStorageKey(meta, "System", "Account", signature.TestKeyringPairAlice.PublicKey, nil) - if err != nil { - return fmt.Errorf("cannot create storage key:%w", err) - } - - var accountInfo types.AccountInfo - ok, err := api.RPC.State.GetStorageLatest(key, &accountInfo) - if err != nil || !ok { - return fmt.Errorf("cannot get latest storage:%w", err) - } - - nonce := uint32(accountInfo.Nonce) - options := types.SignatureOptions{ - BlockHash: genesisHash, - Era: types.ExtrinsicEra{IsMortalEra: false}, - GenesisHash: genesisHash, - Nonce: types.NewUCompactFromUInt(uint64(nonce)), - SpecVersion: rv.SpecVersion, - Tip: types.NewUCompactFromUInt(0), - AppID: types.NewUCompactFromUInt(uint64(0)), - TransactionVersion: rv.TransactionVersion, - } - - err = ext.Sign(keyringPair, options) - if err != nil { - return fmt.Errorf("cannot sign:%w", err) - } - - // Send the extrinsic - sub, err := api.RPC.Author.SubmitAndWatchExtrinsic(ext) - if err != nil { - return fmt.Errorf("cannot submit extrinsic:%w", err) - } - - defer sub.Unsubscribe() - timeout := time.After(100 * time.Second) - for { - // get status of the submitted transaction - select { - case status := <-sub.Chan(): - if status.IsInBlock { - fmt.Printf("\nTxn inside block %v\n", status.AsInBlock.Hex()) - } - if status.IsFinalized { - fmt.Printf("\nTxn finalized %v\n", status.AsFinalized.Hex()) - return nil - } - if status.IsDropped || status.IsInvalid { - fmt.Printf("unexpected extrinsic status from Avail: %#v", status) - } - - case <-timeout: - fmt.Printf("timeout of 100 seconds reached without getting finalized status for extrinsic") - return fmt.Errorf("timeout") - } - } - -} - -func main() { - var configJSON string - var config config.Config - flag.StringVar(&configJSON, "config", "", "config json file") - flag.Parse() - - if configJSON == "" { - log.Println("No config file provided. Exiting...") - os.Exit(0) - } - - err := config.GetConfig(configJSON) - if err != nil { - panic(fmt.Sprintf("cannot get config:%v", err)) - } - - api, err := gsrpc.NewSubstrateAPI(config.ApiURL) - if err != nil { - panic(fmt.Sprintf("cannot create api client:%v", err)) - } - - fmt.Printf("Sending amount %d....", config.Amount) - err = transfer(api, config.Seed, config.Dest, uint64(math.Pow(10, 18))*config.Amount) - if err != nil { - panic(fmt.Sprintf("cannot create transfer:%v", err)) - } -} diff --git a/examples/go/transfer_mortal/main.go b/examples/go/transfer_mortal/main.go deleted file mode 100644 index 1d3443c7d..000000000 --- a/examples/go/transfer_mortal/main.go +++ /dev/null @@ -1,158 +0,0 @@ -package main - -import ( - "avail-gsrpc-examples/internal/config" - "flag" - "fmt" - "log" - "math" - "os" - "time" - - gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" - "github.com/centrifuge/go-substrate-rpc-client/v4/signature" - "github.com/centrifuge/go-substrate-rpc-client/v4/types" - "github.com/vedhavyas/go-subkey/v2" -) - -func transfer(api *gsrpc.SubstrateAPI, senderSeed string, receiver string, amount uint64) error { - - meta, err := api.RPC.State.GetMetadataLatest() - if err != nil { - return fmt.Errorf("cannot get metadata:%w", err) - } - - _, pubkeyBytes, _ := subkey.SS58Decode(receiver) - hexString := subkey.EncodeHex(pubkeyBytes) - - dest, err := types.NewMultiAddressFromHexAccountID(hexString) - if err != nil { - return fmt.Errorf("cannot create address from given hex:%w", err) - } - - balanceCall, err := types.NewCall(meta, "Balances.transfer_keep_alive", dest, types.NewUCompactFromUInt(amount)) - if err != nil { - return fmt.Errorf("cannot create balance call:%w", err) - } - - // Create the extrinsic - ext := types.NewExtrinsic(balanceCall) - - genesisHash, err := api.RPC.Chain.GetBlockHash(0) - if err != nil { - return fmt.Errorf("cannot get block hash:%w", err) - } - - rv, err := api.RPC.State.GetRuntimeVersionLatest() - if err != nil { - return fmt.Errorf("cannot runtime version:%w", err) - } - - keyringPair, err := signature.KeyringPairFromSecret(senderSeed, 42) - if err != nil { - return fmt.Errorf("cannot create KeyPair:%w", err) - } - - key, err := types.CreateStorageKey(meta, "System", "Account", signature.TestKeyringPairAlice.PublicKey, nil) - if err != nil { - return fmt.Errorf("cannot create storage key:%w", err) - } - - var accountInfo types.AccountInfo - ok, err := api.RPC.State.GetStorageLatest(key, &accountInfo) - if err != nil || !ok { - return fmt.Errorf("cannot get latest storage:%w", err) - } - latestHash, err := api.RPC.Chain.GetBlockHashLatest() - if err != nil { - panic(fmt.Sprintf("cannot get Latest Block Hash:%w", err)) - } - latestBlock, err := api.RPC.Chain.GetBlockLatest() - if err != nil { - panic(fmt.Sprintf("cannot get Latest Block:%w", err)) - } - var e types.ExtrinsicEra - e.IsMortalEra = true - number := latestBlock.Block.Header.Number - second := types.U64(number) % 128 - // Uncomment the following for autoassigning the phase value based on period and blockNumber - // mortl := types.NewMortalEra(number, types.U64(256)) - // e.AsMortalEra = mortl - - e.AsMortalEra = types.MortalEra{First: types.U64(128), Second: types.U64(second)} - - nonce := uint32(accountInfo.Nonce) - options := types.SignatureOptions{ - BlockHash: latestHash, - Era: e, - GenesisHash: genesisHash, - Nonce: types.NewUCompactFromUInt(uint64(nonce)), - SpecVersion: rv.SpecVersion, - Tip: types.NewUCompactFromUInt(0), - AppID: types.NewUCompactFromUInt(uint64(0)), - TransactionVersion: rv.TransactionVersion, - } - - err = ext.Sign(keyringPair, options) - if err != nil { - return fmt.Errorf("cannot sign:%w", err) - } - - // Send the extrinsic - sub, err := api.RPC.Author.SubmitAndWatchExtrinsic(ext) - if err != nil { - return fmt.Errorf("cannot submit extrinsic:%w", err) - } - - defer sub.Unsubscribe() - timeout := time.After(100 * time.Second) - for { - // get status of the submitted transaction - select { - case status := <-sub.Chan(): - if status.IsInBlock { - fmt.Printf("\nTxn inside block %v\n", status.AsInBlock.Hex()) - } - if status.IsFinalized { - fmt.Printf("\nTxn finalized %v\n", status.AsFinalized.Hex()) - return nil - } - if status.IsDropped || status.IsInvalid { - fmt.Printf("unexpected extrinsic status from Avail: %#v", status) - } - - case <-timeout: - fmt.Printf("timeout of 100 seconds reached without getting finalized status for extrinsic") - return fmt.Errorf("timeout") - } - } - -} - -func main() { - var configJSON string - var config config.Config - flag.StringVar(&configJSON, "config", "", "config json file") - flag.Parse() - - if configJSON == "" { - log.Println("No config file provided. Exiting...") - os.Exit(0) - } - - err := config.GetConfig(configJSON) - if err != nil { - panic(fmt.Sprintf("cannot get config:%v", err)) - } - - api, err := gsrpc.NewSubstrateAPI(config.ApiURL) - if err != nil { - panic(fmt.Sprintf("cannot create api client:%v", err)) - } - - fmt.Printf("Sending amount %d....", config.Amount) - err = transfer(api, config.Seed, config.Dest, uint64(math.Pow(10, 18))*config.Amount) - if err != nil { - panic(fmt.Sprintf("cannot create transfer:%v", err)) - } -} diff --git a/examples/go/transfer_query_advanced/main.go b/examples/go/transfer_query_advanced/main.go deleted file mode 100644 index 1849b3c2d..000000000 --- a/examples/go/transfer_query_advanced/main.go +++ /dev/null @@ -1,424 +0,0 @@ -package main - -import ( - "avail-gsrpc-examples/internal/config" - "encoding/hex" - "flag" - "fmt" - "log" - "math" - "math/big" - "os" - "strconv" - "strings" - "time" - - gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4" - "github.com/centrifuge/go-substrate-rpc-client/v4/registry" - . "github.com/centrifuge/go-substrate-rpc-client/v4/registry/retriever" - "github.com/centrifuge/go-substrate-rpc-client/v4/registry/state" - "github.com/centrifuge/go-substrate-rpc-client/v4/signature" - "github.com/centrifuge/go-substrate-rpc-client/v4/types" - . "github.com/centrifuge/go-substrate-rpc-client/v4/types/codec" - "github.com/ethereum/go-ethereum/common/hexutil" - "golang.org/x/crypto/blake2b" - - "github.com/vedhavyas/go-subkey" -) - -type InclusionFee struct { - BaseFee BigInt - LenFee BigInt - AdjustedWeightFee BigInt -} - -type BigInt struct { - *big.Int -} - -// UnmarshalJSON defines custom unmarshalling for BigInt. -func (bi *BigInt) UnmarshalJSON(data []byte) error { - // Unquote string (since JSON numbers are sent as strings) - str, err := strconv.Unquote(string(data)) - if err != nil { - return err - } - - // Remove 0x prefix if present and ensure non-empty - str = strings.TrimPrefix(str, "0x") - if str == "" { - return fmt.Errorf("empty string") - } - - // Parse the string - bi.Int = new(big.Int) - _, success := bi.Int.SetString(str, 16) // parse in base 16 - if !success { - return fmt.Errorf("invalid hex string") - } - return nil -} - -// Corresponding to Rust's FeeDetails -type FeeDetails struct { - InclusionFee *InclusionFee -} - -func transfer(api *gsrpc.SubstrateAPI, senderSeed string, receiver string, amount uint64) error { - - meta, err := api.RPC.State.GetMetadataLatest() - if err != nil { - return fmt.Errorf("cannot get metadata:%w", err) - } - - _, pubkeyBytes, _ := subkey.SS58Decode(receiver) - address := subkey.EncodeHex(pubkeyBytes) - - dest, err := types.NewMultiAddressFromHexAccountID(address) - if err != nil { - return fmt.Errorf("cannot create address from given hex:%w", err) - } - - balanceCall, err := types.NewCall(meta, "Balances.transfer_keep_alive", dest, types.NewUCompactFromUInt(amount)) - if err != nil { - return fmt.Errorf("cannot create balance call:%w", err) - } - - // Create the extrinsic - ext := types.NewExtrinsic(balanceCall) - - genesisHash, err := api.RPC.Chain.GetBlockHash(0) - if err != nil { - return fmt.Errorf("cannot get block hash:%w", err) - } - - rv, err := api.RPC.State.GetRuntimeVersionLatest() - if err != nil { - return fmt.Errorf("cannot runtime version:%w", err) - } - keyringPair, err := signature.KeyringPairFromSecret(senderSeed, 42) - if err != nil { - return fmt.Errorf("cannot create KeyPair:%w", err) - } - key, err := types.CreateStorageKey(meta, "System", "Account", keyringPair.PublicKey, nil) - if err != nil { - return fmt.Errorf("cannot create storage key:%w", err) - } - - var accountInfo types.AccountInfo - ok, err := api.RPC.State.GetStorageLatest(key, &accountInfo) - if err != nil || !ok { - return fmt.Errorf("cannot get latest storage:%w", err) - } - - nonce := uint32(accountInfo.Nonce) - options := types.SignatureOptions{ - BlockHash: genesisHash, - Era: types.ExtrinsicEra{IsMortalEra: false}, - GenesisHash: genesisHash, - Nonce: types.NewUCompactFromUInt(uint64(nonce)), - SpecVersion: rv.SpecVersion, - Tip: types.NewUCompactFromUInt(0), - AppID: types.NewUCompactFromUInt(uint64(0)), - TransactionVersion: rv.TransactionVersion, - } - - err = ext.Sign(keyringPair, options) - if err != nil { - return fmt.Errorf("cannot sign:%w", err) - } - - enc, _ := EncodeToHex(ext) - - cleanedHexString := strings.TrimPrefix(enc, "0x") - bytes, err := hex.DecodeString(cleanedHexString) - if err != nil { - log.Fatal(err) - } - hash := blake2b.Sum256(bytes) - ext_z := hexutil.Encode(hash[:]) - // Send the extrinsic - sub, err := api.RPC.Author.SubmitAndWatchExtrinsic(ext) - if err != nil { - return fmt.Errorf("cannot submit extrinsic:%w", err) - } - - defer sub.Unsubscribe() - timeout := time.After(100 * time.Second) - for { - // get status of the submitted transaction - select { - case status := <-sub.Chan(): - if status.IsInBlock { - fmt.Printf("\nTxn inside block %v\n", status.AsInBlock.Hex()) - h := status.AsInBlock - block, err := api.RPC.Chain.GetBlock(h) - if err != nil { - fmt.Printf("err occuerd") - } - var enc string - exts := (block.Block.Extrinsics) - // fmt.Print(ext) - for i, j := range exts { - if j.IsSigned() && j.Method.CallIndex.SectionIndex == 6 { - enc, _ = EncodeToHex(j) - cleanedHexString := strings.TrimPrefix(enc, "0x") - bytes, err := hex.DecodeString(cleanedHexString) - if err != nil { - log.Fatal(err) - } - hash := blake2b.Sum256(bytes) - z := hexutil.Encode(hash[:]) - fmt.Printf("ext hash is %v", z) - if z == ext_z { - fmt.Printf("\n the Transfer extrinsic Index is %d", i+1) - signer := j.Signature.Signer - fmt.Printf("\nfrom address hex %x\n", signer.AsID) - address := fmt.Sprintf("\n%x", j.Method.Args[1:]) - fmt.Printf("to address hex: %s \n", address) - } - } - } - - // key, err := types.CreateStorageKey(meta, "System", "Events", nil, nil) - // if err != nil { - // log.Fatalf("Failed to create storage key: %v", err) - // } - // rawEvents, err := api.RPC.State.GetStorageRaw(key, h) - // if err != nil { - // log.Fatalf("Failed to fetch events: %v", err) - // } - // events := types.EventRecords{} - // err = types.EventRecordsRaw(*rawEvents).DecodeEventRecords(meta, &events) - // if err != nil { - // log.Fatalf("Failed to decode events: %v", err) - // } - - // if rawEvents != nil && len(*rawEvents) > 0 { - // err = types.EventRecordsRaw(*rawEvents).DecodeEventRecords(meta, &events) - // if err != nil { - // log.Fatalf("Failed to decode events: %v", err) - // } - - // for _, e := range events.Balances_Transfer { - - // vals := convInt(fmt.Sprintf("%v", e.Value)) - // fmt.Printf("Transfer event: %v\n", vals) - - // } - // f := events.TransactionPayment_TransactionFeePaid - // for _, i := range f { - // fee := convInt(i.ActualFee.String()) - // fmt.Printf("Fee Paid %v", fee) - // } - - // } else { - // fmt.Println("No events found in the block") - // } - - //Updating the event from generic decoding to registry - retriever, err := NewDefaultEventRetriever(state.NewEventProvider(api.RPC.State), api.RPC.State) - - if err != nil { - fmt.Printf("Couldn't create event retriever: %s", err) - } - events, err := retriever.GetEvents(h) - - if err != nil { - log.Printf("Couldn't retrieve events") - } - for _, event := range events { - if event.Name == "Balances.Transfer" { - from, err := registry.ProcessDecodedFieldValue[*types.AccountID]( - event.Fields, - func(fieldIndex int, field *registry.DecodedField) bool { - - return field.Name == "sp_core.crypto.AccountId32.from" - }, - func(value any) (*types.AccountID, error) { - fields, ok := value.(registry.DecodedFields) - - if !ok { - return nil, fmt.Errorf("unexpected value: %v", value) - } - - accByteSlice, err := registry.GetDecodedFieldAsSliceOfType[types.U8](fields, func(fieldIndex int, field *registry.DecodedField) bool { - return fieldIndex == 0 - }) - - if err != nil { - return nil, err - } - - var accBytes []byte - - for _, accByte := range accByteSlice { - accBytes = append(accBytes, byte(accByte)) - } - - return types.NewAccountID(accBytes) - }, - ) - a := from.ToHexString() - - // // add, _ := types.NewAddressFromHexAccountID(a) - // fmt.Println(from) - fmt.Printf("from address read from event: %s \n", a) - - to, err := registry.ProcessDecodedFieldValue[*types.AccountID]( - event.Fields, - - func(fieldIndex int, field *registry.DecodedField) bool { - - return field.Name == "sp_core.crypto.AccountId32.to" - }, - func(value any) (*types.AccountID, error) { - fields, ok := value.(registry.DecodedFields) - - if !ok { - return nil, fmt.Errorf("unexpected value: %v", value) - } - - accByteSlice, err := registry.GetDecodedFieldAsSliceOfType[types.U8](fields, func(fieldIndex int, field *registry.DecodedField) bool { - return fieldIndex == 0 - }) - - if err != nil { - return nil, err - } - - var accBytes []byte - - for _, accByte := range accByteSlice { - accBytes = append(accBytes, byte(accByte)) - } - - return types.NewAccountID(accBytes) - }, - ) - if err != nil { - fmt.Printf("TO parsing err: %s\n", err.Error()) - } - fmt.Printf("To address read from event: %s \n", to.ToHexString()) - amount, err := registry.GetDecodedFieldAsType[types.U128]( - event.Fields, - func(fieldIndex int, field *registry.DecodedField) bool { - return fieldIndex == 2 - }, - ) - if err != nil { - fmt.Printf("Amount parsing err: %s\n", err.Error()) - } - fmt.Printf("Amount transferred : %s \n", convInt(amount.String())) - if err != nil { - fmt.Printf("Balances.Deposit.Who: %s\n", err.Error()) - } - } - if event.Name == "TransactionPayment.TransactionFeePaid" { - - amount, err := registry.GetDecodedFieldAsType[types.U128]( - event.Fields, - func(fieldIndex int, field *registry.DecodedField) bool { - return fieldIndex == 1 - }, - ) - if err != nil { - fmt.Printf("Amount parsing err: %s\n", err.Error()) - } - fmt.Printf("Actual Fee from TransactionPayment.TransactionFeePaid event: %s \n", convInt(amount.String())) - if err != nil { - fmt.Printf("Balances.Deposit.Who: %s\n", err.Error()) - } - } - } - var inclusionFee InclusionFee = InclusionFee{ - BaseFee: BigInt{big.NewInt(0)}, - LenFee: BigInt{big.NewInt(0)}, - AdjustedWeightFee: BigInt{big.NewInt(0)}, - } - - var feeDetails FeeDetails = FeeDetails{ - InclusionFee: &inclusionFee, - } - - err = api.Client.Call(&feeDetails, "payment_queryFeeDetails", enc, h) - if err != nil { - panic(fmt.Sprintf("%v\n", err)) - } - - fmt.Printf("Base Fee: %s\n", formatFee(inclusionFee.BaseFee, false)) - fmt.Printf("Length Fee: %s\n", formatFee(inclusionFee.LenFee, true)) - fmt.Printf("Adjusted Weight Fee: %s\n", formatFee(inclusionFee.AdjustedWeightFee, false)) - - return nil - } - - case <-timeout: - fmt.Printf("timeout of 100 seconds reached without getting finalized status for extrinsic") - return fmt.Errorf("timeout") - } - } - -} -func formatFee(fee BigInt, isLenFee bool) string { - var value float64 - var unit string - - feeFloat := new(big.Float).SetInt(fee.Int) - - if isLenFee { - - feeFloat.Quo(feeFloat, big.NewFloat(1e12)) - unit = "µAVAIL" - } else { - - feeFloat.Quo(feeFloat, big.NewFloat(1e15)) - unit = "mAVAIL" - } - - value, _ = feeFloat.Float64() - - return fmt.Sprintf("%.4f %s", value, unit) -} -func convInt(val string) string { - bigIntValue := new(big.Int) - bigIntValue.SetString(val, 10) - - divisor := new(big.Int) - divisor.SetString("1000000000000000000", 10) - - bigFloatValue := new(big.Float).SetInt(bigIntValue) - divisorFloat := new(big.Float).SetInt(divisor) - result := new(big.Float).Quo(bigFloatValue, divisorFloat) - - x := (result.Text('f', 18)) - return x -} - -func main() { - var configJSON string - var config config.Config - flag.StringVar(&configJSON, "config", "", "config json file") - flag.Parse() - - if configJSON == "" { - log.Println("No config file provided. Exiting...") - os.Exit(0) - } - - err := config.GetConfig(configJSON) - if err != nil { - panic(fmt.Sprintf("cannot get config:%v", err)) - } - - api, err := gsrpc.NewSubstrateAPI(config.ApiURL) - if err != nil { - panic(fmt.Sprintf("cannot create api client:%v", err)) - } - - fmt.Printf("Sending amount %d....", config.Amount) - err = transfer(api, config.Seed, config.Dest, uint64(math.Pow(10, 18))*config.Amount) - if err != nil { - panic(fmt.Sprintf("cannot create transfer:%v", err)) - } -}