Acala Wiki
LearnBuildMaintain
  • Acala & Karura Wiki
  • Acala 2.0
    • Overview
    • Execution Roadmap
    • aUSD SEED (aSEED)
      • aSEED Integration Guide
    • ACA
      • ACA/KAR Staking
    • Universal Asset Hub (UAH)
    • Euphrates DApp
  • Learn
    • Acala Introduction
      • Acala Dollar
      • AcalaSwap
      • Homa Liquid Staking
      • Redenomination of ACA
    • Trilogy Networks
    • Flexible Fees
      • How to change default fee token
    • Governance Overivew
      • Participate in Democracy
      • How to Verify a Runtime Upgrade
      • How to Verify Referendum Proposal
    • Treasury
    • Acala EVM+
      • Why Acala EVM+
      • Existing Solutions
      • Acala EVM+
        • Composable DeFi Stack
        • Flexible Fees
        • EVM Account
        • On-chain Scheduler
        • Queryable & Lightweight
        • Upgradable Contracts
        • Compatible Toolings
        • No Dust Account
      • How does it work?
  • Get started
    • Acala Network
      • Acala Launch Phases
      • Wallet & Account
        • Account Generation
          • Polkadot{.js} Browser Extension
          • Polkawallet Mobile App
          • Ledger Hardware Wallet
        • Exchange Withdraw/Deposit
        • Balance Type and Vesting
        • Check Address for Different Chains
      • Acala's DOT Bridge
      • Acala Assets
      • Governance
      • Acala Web Apps
      • Acala Mobile
    • Karura Network
      • Karura Launch Phases
      • Check Distribution
      • Wallets & Account
        • Account Generation
          • Polkadot{.js} Browser Extension
          • Polkawallet Mobile App
          • Ledger Hardware Wallet
        • Exchange Withdraw/Deposit
        • Balance Type & Vesting
        • Check Address for Different Chains
      • Inter Kusama Transfer
      • Karura Assets
      • Karura Web Apps
      • Governance
      • Transaction Fees
    • Mandala Testnet
  • Integrate
    • Acala Network
      • Protocol Info
      • Token Transfer
      • Node Interaction
      • Full Node
      • Collator
    • Karura Network
      • Protocol Info
      • Token Transfer
      • Node Interaction
      • Full Node
      • Collator
  • Build
    • EVM+ documentation
    • SDKs
      • Acala Stablecoin
      • Homa Liquid Staking
      • AcalaSwap
      • Homa Staking
    • Guides
      • Node Interaction
      • Transaction Construction
    • Build DApps
      • Deploy Ecosystem Modules
      • Composable Chains
        • Open HRMP Channel
        • Bridge Tokens
        • Cross-chain Listing
      • Open-Web3-Stack & ORML
      • Smart Contracts
        • Acala EVM Overview
        • Get Started
          • Connect to a Node
            • Use MetaMask with EVM+
          • Setup EVM Account
          • Polkadot Explorer
          • EVM Playground
          • Use Remix
          • Use Waffle
          • Use Hardhat
          • Deploy Contracts
        • Advanced
          • Use Native & Cross-chain Tokens
          • Use On-chain Scheduler
            • Tutorial
          • Use Oracle Feeds
          • Upcoming Features
        • Acala EVM Hacker Guide
          • Try Acala DApp
    • Tools
    • Indexers
      • SubQuery
    • Miscellaneous
      • Runtime Events
      • Modules
    • Cross Chain DeFi
  • Ecosystem
    • General Info
    • Alliance
    • Community
    • Contribution & Rewards
      • Acala Old Friend NFT
    • Ecosystem
    • Media Kits
    • Ledger Generic App Migration
  • Crowdloans
    • What are crowdloans
    • Acala Crowdloan
      • Crowdloan Event
      • Crowdloan Rewards
      • Claim ACA
      • How to Crowdloan
      • FAQ
      • Acala Quests
        • Attaching Polkawallet to Polkadot.js Extension
      • DOT Address
        • Creating a New DOT Account
        • Becoming a DOT Holder
        • Unstaking Your DOT
          • Unstaking Your DOT Tokens on Polkadot.{js} Extension
          • Unstaking Your DOT Tokens on Polkawallet
    • Karura Crowdloan
      • Crowdloan Event
      • Crowdloan Rewards
      • Claim KAR
      • How to Crowdloan
      • FAQ
      • KSM Address
        • Creating a New KSM Account
        • Check KSM Address
        • Unstaking Your KSM
          • Unstaking Your KSM Tokens on Polkawallet
          • Unstaking Your KSM Tokens on Polkadot.{js} Extension
      • Finding Tokens and NFTs
  • Misc
    • aUSD Incident
    • Contributing
    • Glossary
Powered by GitBook
On this page
  • Homa Example
  • prepare env, api, and sdk
  • stake DOT
  • unstake LDOT
  • More References

Was this helpful?

Edit on GitHub
  1. Build
  2. SDKs

Homa Liquid Staking

PreviousAcala StablecoinNextGuides

Last updated 1 year ago

Was this helpful?

To interact with Acala or Karura from Javascript you can use @polkadot/api along with @acala-network/api.

For those looking to stake DOT or unstake LDOT, we also offer the .

Homa Example

The following example demonstrates how to utilize the Homa SDK and the Polkadot API for staking DOT and unstaking LDOT.

You can fork the on replit and run the example yourself.

To run the example on replit, you neeed to first fork it, otherwise it will only show an empty webview page.

Actual result will be logged to console instead of the webview page, only after you fork it.

prepare env, api, and sdk

first import deps

const { fetchConfig, setupWithServer } = require('@acala-network/chopsticks')
const { ApiPromise, WsProvider } = require('@polkadot/api')
const { Homa, Wallet } = require('@acala-network/sdk')
const { createTestKeyring } = require('@polkadot/keyring')

use Chopsticks to fork mainnet to a local testnet

const acalaConfig = await fetchConfig('acala')
acalaConfig.port = 8111 // set the port
acalaConfig.block = 4286000
acalaConfig.db = './db.sqlite'
const { chain, listenPort, close } = await setupWithServer(acalaConfig)

// This server can be accessed at wss://acalajs-examples--xlc.repl.co/
// Use https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Facalajs-examples--xlc.repl.co to connect to this testnet
console.log('Chopsticks is running on port', listenPort)
------------------------------------------- console -------------------------------------------
INFO (14701): Loading config file https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/acala.yml
2023-08-23 14:22:26        REGISTRY: Unknown signed extensions SetEvmOrigin found, treating them as no-effect
Chopsticks is running on port 8111
INFO (rpc/14701): Acala RPC listening on port 8111

setup Acala api connect to the local testnet

const provider = new WsProvider(`ws://localhost:${listenPort}`)
const api = new ApiPromise({ provider, noInitWarn: true })
await api.isReady

add a couple helpers

// send a transaction and wait for it to be included
const sendTxAndWait = (tx) => {
  return new Promise((resolve) => {
    tx.send((status) => {
      if (status.isInBlock || status.isFinalized) {
        resolve(status.events)
      }
    })
  })
}

// sign tx with a mock signature that will be accepted by Chopsticks
// so we can act on behalf of any accounts without the private key
// this requires mockSignatureHost to be enabled
const fakeSign = (tx, addr, nonce) => {
  const mockSignature = new Uint8Array(64)
  mockSignature.fill(0xcd)
  mockSignature.set([0xde, 0xad, 0xbe, 0xef])
  tx.signFake(addr, {
    nonce,
    genesisHash: api.genesisHash,
    runtimeVersion: api.runtimeVersion,
    blockHash: api.genesisHash,
  })
  // update fake signature
  tx.signature.set(mockSignature)

  return tx
}

setup wallet sdk and homa sdk

const wallet = new Wallet(api)
await wallet.isReady

const homa = new Homa(api, wallet)
await homa.isReady

query some info

// treasury address
const address = '23M5ttkmR6KcoTAAE6gcmibnKFtVaTP5yxnY8HF1BmrJ2A1i'

console.log('Address', address)

const ldot = { Token: 'LDOT' }

// check LDOT balance
const balance = await api.query.tokens.accounts(address, ldot)
const free = balance.free.toNumber()

console.log('LDOT Balance:', free / 10 ** 10)

// get exchange rate
const exchangeRate = (await homa.getEnv()).exchangeRate.toNumber()
console.log('Exchange Rate:', exchangeRate)

// calculate DOT amount
const dotAmount = free * exchangeRate
console.log('DOT amount:', dotAmount / 10 ** 10)
------------------------------------------- console -------------------------------------------
Address 23M5ttkmR6KcoTAAE6gcmibnKFtVaTP5yxnY8HF1BmrJ2A1i
LDOT Balance: 21.1962713105
Exchange Rate: 0.13037338530710973
DOT amount: 2.7634296466378525

stake DOT

// Bob
const testaddr = '246gNkjCexYRsCpdjtVhz35sHjcb21jpqipzT9u4uwKV8iEE'

// use Chopsticks to mint this user some ACA and DOT
await api.rpc('dev_setStorage', {
  System: {
    Account: [
      [
        // key
        [testaddr],
        // value
        {
          providers: 2, // 1 for ACA, 1 for DOT
          data: {
            free: 1000 * 1e12 // 1000 ACA
          }
        }
      ]
    ]
  },
  Tokens: {
    Accounts: [
      [
        // key
        [
          testaddr,
          { Token: 'DOT' }
        ],
        // value
        {
          free: 100 * 10e10 // 100 DOT
        }
      ]
    ]
  }
})

{
  // create tx to use 100 DOT to mint LDOT
  const tx = api.tx.homa.mint(100 * 10e10)
  fakeSign(tx, testaddr, 0)

  console.log('\nSend tx to use 100 DOT to mint LDOT')
  const events = await sendTxAndWait(tx)
  console.log('tx events', events.map(x => x.event.toHuman()))
}

const ldotBalance = (await api.query.tokens.accounts(testaddr, ldot)).free.toNumber()
console.log('\nMinted', ldotBalance / 1e10, 'LDOT')
------------------------------------------- console -------------------------------------------
Send tx to use 100 DOT to mint LDOT
INFO (block-builder/14701): Try building block #4,286,001
    number: 4286001
    extrinsicsCount: 1
    umpCount: 0
tx events [
  <many tx events>
]

Minted 7667.5245218183 LDOT

unstake LDOT

There are number of ways to unstake LDOT / convert it back to DOT

  1. Slow unstake by wait for 28 days.

  2. Use fast redeem by matching with new stakers. A fast redeem fee is charged. This is only available if there are DOT in the pending pool.

  3. Use the Taiga tDOT pool to swap LDOT to DOT. A swap fee is charged. The swap rate depends on the pool balance

slow unstake

// unstake half 
const unstakeAmount = Math.floor(ldotBalance / 2)

{
  // create tx to unstake without fast redeem. i.e. wait for 28 days unstaking period
  const tx = api.tx.homa.requestRedeem(unstakeAmount, false /* true to allow fast redeem if possible */)
  fakeSign(tx, testaddr, 1)

  console.log('Send tx to unstake', unstakeAmount / 1e10, 'LDOT')
  const events = await sendTxAndWait(tx)
  console.log('tx events', events.map(x => x.event.toHuman()))
}

// the redeem request is still pending and not yet enacted on relaychain
const pendingRedeemLDOT = (await api.query.homa.redeemRequests(testaddr))
console.log('\nPending redeem LDOT amount', pendingRedeemLDOT.toHuman())

const keyring = createTestKeyring()
const sudoKey = keyring.getPair('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY') // Alice

// simulate relaychain era bump
// this will trigger XCM to do unstake on relaychain
await sendTxAndWait(await api.tx.sudo.sudo(api.tx.homa.forceBumpCurrentEra(1)).signAsync(sudoKey))

const unbondings = await api.query.homa.unbondings.entries(testaddr)
console.log('\nUnbonding requests', unbondings.map(([key, value]) => [key.toHuman(), value.toHuman()]))

// bump 28 era so that the DOT is available for withdraw
await sendTxAndWait(await api.tx.sudo.sudo(api.tx.homa.forceBumpCurrentEra(28)).signAsync(sudoKey))

await sendTxAndWait(fakeSign(api.tx.homa.claimRedemption(testaddr /* any account can trigger redeem */), testaddr, 2))

const dotBalance = (await api.query.tokens.accounts(testaddr, { token: 'DOT'})).free.toNumber()
console.log('\nRedeemed', dotBalance / 1e10, 'DOT')
------------------------------------------- console -------------------------------------------
Send tx to unstake 3833.7622609091 LDOT
INFO (block-builder/14701): Block built
    number: 4286001
    hash: "0xaaec1a0bc862a76af6b53bcc60c7b62256487cce5c468828360950491d281d7f"
    extrinsics: [
      "0xbd0184008eaf04151687736326c9fea1…cdcdcdcd00000074000b00a0724e1809"
    ]
    pendingExtrinsicsCount: 0
    ump: {}
INFO (block-builder/14701): Try building block #4,286,002
    number: 4286002
    extrinsicsCount: 1
    umpCount: 0
tx events [
  <tx events>
]

Pending redeem LDOT amount [ '38,337,622,609,091', false ]
INFO (block-builder/14701): Block built
    number: 4286002
    hash: "0xed20da2f91d4a5f2d3104c2c25bd3005d965a4837e0ccb90f9bba865b6b0c2fd"
    extrinsics: [
      "0xc10184008eaf04151687736326c9fea1…cdcdcd00040074010bc38c602cde2200"
    ]
    pendingExtrinsicsCount: 0
    ump: {}
INFO (block-builder/14701): Try building block #4,286,003
    number: 4286003
    extrinsicsCount: 1
    umpCount: 0

Unbonding requests [
  [
    [ '246gNkjCexYRsCpdjtVhz35sHjcb21jpqipzT9u4uwKV8iEE', '1,205' ],
    '4,999,981,935,420'
  ]
]
INFO (block-builder/14701): Block built
    number: 4286003
    hash: "0xece387f4671b9245969411be150f01958642c28b8e663c8b374dc24941e361d4"
    extrinsics: [
      "0xbd018400d43593c715fdd31c61141abd…6d6e0a8924010000ff00740801000000"
    ]
    pendingExtrinsicsCount: 0
    ump: {}
INFO (block-builder/14701): Try building block #4,286,004
    number: 4286004
    extrinsicsCount: 1
    umpCount: 0
INFO (block-builder/14701): Block built
    number: 4286004
    hash: "0x6509c89c4bb36e7181a0d0016ec80c74bed40faa967b0179d7c149d0e0212baf"
    extrinsics: [
      "0xbd018400d43593c715fdd31c61141abd…e53ea68134010400ff0074081c000000"
    ]
    pendingExtrinsicsCount: 0
    ump: {}
INFO (block-builder/14701): Try building block #4,286,005
    number: 4286005
    extrinsicsCount: 1
    umpCount: 0

Redeemed 499.998193542 DOT

swap with taiga tDOT pool

use aggregatedDex to perform swap, which allow multiple step swap with both Taiga pool and Acala Dex pool in a single tx

const tx = api.tx.aggregatedDex.swapWithExactSupply(
  [{Taiga: [/*pool id*/ 0, /*supply asset*/1, /*target asset*/0]}],
  unstakeAmount,
  0 // should always set a min target to prevent unexpected result
)
fakeSign(tx, testaddr, 3)

console.log('Send tx to swap', unstakeAmount / 1e10, 'LDOT')
const events = await sendTxAndWait(tx)
console.log('tx events', events.map(x => x.event.toHuman()))
------------------------------------------- console -------------------------------------------
Send tx to swap 3833.7622609091 LDOT
INFO (block-builder/14701): Block built
    number: 4286005
    hash: "0xf62a24768622c45aca7411c77df101dffb934f2a4d63dfd19dc3ff795e008f71"
    extrinsics: [
      "0x210284008eaf04151687736326c9fea1…87613693c912909cb226aa4794f26a48"
    ]
    pendingExtrinsicsCount: 0
    ump: {}
INFO (block-builder/14701): Try building block #4,286,006
    number: 4286006
    extrinsicsCount: 1
    umpCount: 0
tx events [
  <tx events>
]
INFO (block-builder/14701): Block built
    number: 4286006
    hash: "0xcd8e6b58affca45dae23d1321cf0ff22897d0ae8782c6703bcf31ccba8a0accb"
    extrinsics: [
      "0xf90184008eaf04151687736326c9fea1…01000000000000000bc38c602cde2200"
    ]
    pendingExtrinsicsCount: 0
    ump: {}

clean up

shutdown the Chopsticks server

await close()

More References

Homa source code can be found .

You can learn more about @polkadot/api .

Homa SDK
live example
here
here
chopsticks doc