Skip to content

Commit

Permalink
Added multisig, account abstraction, block abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
markopoloparadox committed Oct 28, 2024
1 parent 2e467f8 commit 76e76d1
Show file tree
Hide file tree
Showing 25 changed files with 1,522 additions and 596 deletions.
55 changes: 55 additions & 0 deletions avail-js/docs/advanced_examples/account_abstraction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { SDK, Keyring, Account, WaitFor } from "./../../src/index"

const main = async () => {
const providerEndpoint = "ws://127.0.0.1:9944"
const sdk = await SDK.New(providerEndpoint)
const alice = new Keyring({ type: "sr25519" }).addFromUri("//Alice")

/*
Sometimes the existing sdk functions can be too low level and cumbersome to use.
The Account class tries to remedy that by providing a similar interface but
with less parameters.
Account related data like balance, nonce and associated app keys can all be accessed
by calling `getBalance`, `getNonceState`, and `getAppKeys` respectively.
*/
const account = new Account(sdk, alice)
console.log((await account.getBalance()).free.toString())
console.log(await account.getNonceNode())
console.log(await account.getNonceState())
console.log(await account.getAppKeys())

/*
Three most common transactions are as well part of the Account interface.
`balanceTransfer` (`balanceTransferNoWait`)
`createApplicationKey` (`createApplicationKeyNoWait`)
`submitData` (`submitDataNoWait`)
*/
const _r1 = await account.balanceTransfer("5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw", account.oneAvail())
const r2 = await account.createApplicationKey("Alice Key")
if (r2.isErr) {
process.exit(1)
}

/*
Setting app id, nonce, and the waitfor method can be easily done by calling
`setAppId`, `setNonce`, and `setWaitFor` respectively.
These values are sticky which means they will persist until you change them again.
*/
account.setAppId(parseInt(r2.event.id))
account.setNonce(await account.getNonceNode())
account.setWaitFor(WaitFor.BlockInclusion)
const _r3 = await account.submitData("My Data")

/*
To make sure that we don't use the same app id and the same nonce for our next
call we reset them to null (default value)
*/
account.setAppId(null)
account.setNonce(null)
const _r4 = await account.submitDataNoWait("My Data")

process.exit()
}
main()
82 changes: 82 additions & 0 deletions avail-js/docs/advanced_examples/block_abstraction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { SDK, Block, sdkBlock, Keyring, Account } from "./../../src/index"

const main = async () => {
const providerEndpoint = "ws://127.0.0.1:9944"
const sdk = await SDK.New(providerEndpoint)
const account = new Account(sdk, new Keyring({ type: "sr25519" }).addFromUri("//Alice"))

// Submitting data that we will query later
const tx = await account.submitData("aabbcc")
if (tx.isErr) {
process.exit(0)
}
const { blockHash, txHash, txIndex } = tx
console.log("Reference hex value: " + tx.txData.data)

/*
Data Submissions from a block can be fetched in two ways:
- Using the Block object
- Using helper functions
*/

/*
The block abstraction object has helper functions to get data from
submit data transactions.
*/
const block = await Block.New(sdk.api, blockHash)
const r1 = block.getSubmitDataAll()
console.log(r1)
const r2 = block.getSubmitDataHash(txHash)
if (r2.isOk()) {
console.log(r2.value)
}
const r3 = block.getSubmitDataIndex(txIndex)
if (r3.isOk()) {
console.log(r3.value)
}

/// The Signed block can be accessed if necessary.
const _signedBlock = block.signedBlock

/*
The same interface can be found as free functions inside
sdkBlock namespace.
Block can as well be instated if `signedBlock` is available.
const _block = new Block(signedBlock)
*/
const signedBlock = await sdk.api.rpc.chain.getBlock(blockHash)
const r4 = sdkBlock.getSubmitDataAll(signedBlock)
console.log(r4)
const r5 = sdkBlock.getSubmitDataHash(signedBlock, txHash)
if (r5.isOk()) {
console.log(r5.value)
}
const r6 = sdkBlock.getSubmitDataIndex(signedBlock, txIndex)
if (r6.isErr()) {
process.exit(1)
}
const dataSubmission = r6.value

/*
DataSubmission structure:
{
"txHash": "0x2965fa2a2df6f818f0725be2b92b0dd399f1cf1e1620e1477fcc2b3e9ba9f491",
"txIndex": 1,
"hexData": "616162626363"
"txSigner": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
}
*/
console.log("Tx Hash: " + dataSubmission.txHash.toString())
console.log("Tx Index: " + dataSubmission.txIndex)
console.log("Hex data: " + dataSubmission.hexData)
console.log("Tx Signer: " + dataSubmission.txSigner)

/*
There is a helper function to get the Ascii version of the hex data
*/
console.log("Ascii data: " + dataSubmission.toAscii())

process.exit()
}
main()
152 changes: 152 additions & 0 deletions avail-js/docs/advanced_examples/multisig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { SDK, WaitFor, Keyring, BN, KeyringPair, Weight, TxResultDetails, MultisigTimepoint } from "./../../src/index"

const main = async () => {
const providerEndpoint = "ws://127.0.0.1:9944"
const sdk = await SDK.New(providerEndpoint)

// Multisig Signatures
const alice = new Keyring({ type: "sr25519" }).addFromUri("//Alice")
const bob = new Keyring({ type: "sr25519" }).addFromUri("//Bob")
const charlie = new Keyring({ type: "sr25519" }).addFromUri("//Charlie")

// Create Multisig Account
const threshold = 3
const multisigAddress = sdk.util.generateMultisig([alice.address, bob.address, charlie.address], threshold)
await fundMultisigAccount(sdk, alice, multisigAddress)

// Define what action will be taken by the multisig account
const amount = SDK.oneAvail()
const call = sdk.api.tx.balances.transferKeepAlive(multisigAddress, amount)
// Data needed for multisig approval and execution
const callHash = call.method.hash.toString()
const callData = call.unwrap().toHex()
const maxWeight = (await call.paymentInfo(alice.address)).weight

/*
The first signature creates and approves the multisig transaction. All the next signatures (besides the last one) should
use the `nextApproval` function to approve the tx. The last signature should use the `lastApproval` function to approve
and execute the multisig tx.
In practice it means the following:
- If the threshold is 2 do the following:
- firstApproval
- lastApproval
- If the threshold is 4 do the following:
- firstApproval
- nextApproval
- nextApproval
- lastApproval
*/

// Create New Multisig
const call1signatures = sdk.util.sortMultisigAddresses([bob.address, charlie.address])
const firstResult = await firstApproval(sdk, alice, threshold, call1signatures, callHash, maxWeight)

// Approve existing Multisig
const timepoint: MultisigTimepoint = { height: firstResult.blockNumber, index: firstResult.txIndex }
const call2signatures = sdk.util.sortMultisigAddresses([alice.address, charlie.address])
const _secondResult = await nextApproval(sdk, bob, threshold, call2signatures, timepoint, callHash, maxWeight)

// Execute Multisig
const call3signatures = sdk.util.sortMultisigAddresses([alice.address, bob.address])
const _thirdResult = await lastApproval(sdk, charlie, threshold, call3signatures, timepoint, callData, maxWeight)

process.exit()
}

async function fundMultisigAccount(sdk: SDK, alice: KeyringPair, multisigAddress: string): Promise<string> {
console.log("Funding multisig account...")
const amount = new BN(10).pow(new BN(18)).mul(new BN(100)) // 100 Avail
const result = await sdk.tx.balances.transferKeepAlive(multisigAddress, amount, WaitFor.BlockInclusion, alice)
if (result.isErr) {
console.log(result.reason)
process.exit(1)
}

return multisigAddress
}

async function firstApproval(
sdk: SDK,
account: KeyringPair,
threshold: number,
otherSignatures: string[],
callHash: string,
maxWeight: Weight,
): Promise<TxResultDetails> {
console.log("Alice is creating a Multisig Transaction...")

const maybeTxResult = await sdk.tx.multisig.approveAsMulti(
threshold,
otherSignatures,
null,
callHash,
maxWeight,
WaitFor.BlockInclusion,
account,
)
if (maybeTxResult.isErr()) {
console.log(maybeTxResult.error)
process.exit(1)
}

return maybeTxResult.value.details
}

async function nextApproval(
sdk: SDK,
account: KeyringPair,
threshold: number,
otherSignatures: string[],
timepoint: MultisigTimepoint,
callHash: string,
maxWeight: Weight,
): Promise<TxResultDetails> {
console.log("Bob is approving the existing Multisig Transaction...")

const maybeTxResult = await sdk.tx.multisig.approveAsMulti(
threshold,
otherSignatures,
timepoint,
callHash,
maxWeight,
WaitFor.BlockInclusion,
account,
)
if (maybeTxResult.isErr()) {
console.log(maybeTxResult.error)
process.exit(1)
}

return maybeTxResult.value.details
}

async function lastApproval(
sdk: SDK,
account: KeyringPair,
threshold: number,
otherSignatures: string[],
timepoint: MultisigTimepoint,
callData: string,
maxWeight: Weight,
): Promise<TxResultDetails> {
console.log("Charlie is approving and executing the existing Multisig Transaction...")

const maybeTxResult = await sdk.tx.multisig.asMulti(
threshold,
otherSignatures,
timepoint,
callData,
maxWeight,
WaitFor.BlockInclusion,
account,
)
if (maybeTxResult.isErr()) {
console.log(maybeTxResult.error)
process.exit(1)
}

return maybeTxResult.value.details
}

main()
29 changes: 29 additions & 0 deletions avail-js/docs/advanced_examples/nonce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { SDK, WaitFor, Keyring, TransactionOptions } from "./../../src/index"

const main = async () => {
const providerEndpoint = "ws://127.0.0.1:9944"
const sdk = await SDK.New(providerEndpoint)

const alice = new Keyring({ type: "sr25519" }).addFromUri("//Alice")

/*
The SDK provides two easy to use but slightly different nonce functions.
- `getNonceState` - Returns nonce from the state of network. This only changes
on a per block basis
- `getNonceNode` - Returns nonce from the node's storage. This gets the nonce
from the state and it updates it if there are existing
transactions already in the mem pool.
If in doubt, use `getNonceNode`.
*/
let nonceState1 = await sdk.util.getNonceState(alice.address)
let nonceNode1 = await sdk.util.getNonceNode(alice.address)
console.log(`Nonce State: ${nonceState1}, Nonce Node: ${nonceNode1}`)
let _result = await sdk.tx.dataAvailability.submitDataNoWait("Data", alice)
let nonceState2 = await sdk.util.getNonceState(alice.address)
let nonceNode2 = await sdk.util.getNonceNode(alice.address)
console.log(`Nonce State: ${nonceState2}, Nonce Node: ${nonceNode2}`)

process.exit()
}
main()
26 changes: 26 additions & 0 deletions avail-js/docs/advanced_examples/transaction_options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { SDK, WaitFor, Keyring, TransactionOptions } from "./../../src/index"

const main = async () => {
const providerEndpoint = "ws://127.0.0.1:9944"
const sdk = await SDK.New(providerEndpoint)
const alice = new Keyring({ type: "sr25519" }).addFromUri("//Alice")

/*
Setting up application id and/or nonce for any transaction is as simple as just defining them
and passing them as the last argument to any sdk transaction call.
TransactionOptions interface has the following fields:
- app_id?: number
- nonce?: number
- era?: number
- blockHash?: H256
*/
let nonce = await sdk.util.getNonceNode(alice.address)
let options: TransactionOptions = { app_id: 1, nonce }
let result = await sdk.tx.dataAvailability.submitData("Data", WaitFor.BlockInclusion, alice, options)

console.log(JSON.stringify(result, null, 2))

process.exit()
}
main()
17 changes: 17 additions & 0 deletions avail-js/docs/extrinsics/balances_tranfer_all_no_wait.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { SDK, Keyring } from "../../src/index"

const main = async () => {
const providerEndpoint = "ws://127.0.0.1:9944"
const sdk = await SDK.New(providerEndpoint)

// Input
const account = new Keyring({ type: "sr25519" }).addFromUri("//Alice")
const dest = "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw" // Eve
const keepAlive = true

const txHash = await sdk.tx.balances.transferAllNoWait(dest, keepAlive, account)

console.log(JSON.stringify(txHash, null, 2))
process.exit()
}
main()
17 changes: 17 additions & 0 deletions avail-js/docs/extrinsics/balances_tranfer_allow_death_no_wait.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { SDK, Keyring } from "../../src/index"

const main = async () => {
const providerEndpoint = "ws://127.0.0.1:9944"
const sdk = await SDK.New(providerEndpoint)

// Input
const account = new Keyring({ type: "sr25519" }).addFromUri("//Alice")
const dest = "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw" // Eve
const amount = SDK.oneAvail()

const txHash = await sdk.tx.balances.transferAllowDeathNoWait(dest, amount, account)

console.log(JSON.stringify(txHash, null, 2))
process.exit()
}
main()
Loading

0 comments on commit 76e76d1

Please sign in to comment.