Skip to content
This repository was archived by the owner on Sep 14, 2023. It is now read-only.

feat: asset pallet example #1087

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 181 additions & 0 deletions examples/assets.eg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/**
* @title Asset Palette Example
* @stability unstable
* @description Various calls utilizing the asset palette
*/

import { contractsDev } from "@capi/contracts-dev"
import { assertEquals, assertRejects } from "asserts"
import { createDevUsers, is, Rune, Scope } from "capi"
import { signature } from "capi/patterns/signature/polkadot"

const scope = new Scope()

const { alexa, billy, carol } = await createDevUsers()

const ASSET_ID = 0
const textEncoder = new TextEncoder()
const textDecoder = new TextDecoder()

await contractsDev.Assets.create({
id: ASSET_ID,
admin: alexa.address,
minBalance: 1n,
}).signed(signature({ sender: alexa }))
.sent()
.dbgStatus("Create Asset:")
.inBlockEvents()
.run(scope)

await contractsDev.Assets.setMetadata({
id: ASSET_ID,
name: textEncoder.encode("Capi Socks"),
symbol: textEncoder.encode("CAPI"),
decimals: 2,
}).signed(signature({ sender: alexa }))
.sent()
.dbgStatus("Set Metadata:")
.inBlockEvents()
.run(scope)

await contractsDev.Assets.Metadata.value(ASSET_ID, contractsDev.latestBlockHash)
.unhandle(is(undefined))
.map((metadata) => ({
...metadata,
name: textDecoder.decode(metadata.name),
symbol: textDecoder.decode(metadata.symbol),
}))
.dbg("Asset Metadata:")
.run(scope)

await contractsDev.Assets.mint({
id: ASSET_ID,
beneficiary: alexa.address,
amount: 1000n,
}).signed(signature({ sender: alexa }))
.sent()
.dbgStatus("Mint Asset:")
.inBlockEvents()
.unhandleFailed()
.run(scope)

await contractsDev.Assets.transfer({
id: ASSET_ID,
target: billy.address,
amount: 10n,
})
.signed(signature({ sender: alexa }))
.sent()
.dbgStatus("Transfer Asset to Billy:")
.inBlockEvents()
.unhandleFailed()
.run(scope)

await contractsDev.Assets.Account
.value(Rune.tuple([ASSET_ID, billy.publicKey]), contractsDev.latestBlockHash)
.unhandle(is(undefined))
.map(({ balance }) => balance)
.dbg("Billy Asset Balance:")
.run(scope)

await contractsDev.Assets.burn({
id: ASSET_ID,
who: billy.address,
amount: 5n,
}).signed(signature({ sender: alexa }))
.sent()
.dbgStatus("Burn Billy's Assets:")
.inBlockEvents()
.run(scope)

await contractsDev.Assets.Account
.value(Rune.tuple([ASSET_ID, billy.publicKey]), contractsDev.latestBlockHash)
.unhandle(is(undefined))
.map(({ balance }) => balance)
.dbg("Billy Asset Balance Post Burn:")
.run(scope)

await contractsDev.Assets.freeze({
id: ASSET_ID,
who: billy.address,
}).signed(signature({ sender: alexa }))
.sent()
.dbgStatus("Freeze Billy's Assets:")
.inBlockEvents()
.run(scope)

await assertRejects(async () =>
contractsDev.Assets.transfer({
id: ASSET_ID,
target: carol.address,
amount: 1n,
})
.signed(signature({ sender: billy }))
.sent()
.dbgStatus("Billy Transfer Frozen Assets:")
.inBlockEvents()
.unhandleFailed()
.run(scope), "Transfer Frozen Assets")

await contractsDev.Assets.thaw({
id: ASSET_ID,
who: billy.address,
}).signed(signature({ sender: alexa }))
.sent()
.dbgStatus("Thaw Billy's Assets:")
.inBlockEvents()
.run(scope)

await contractsDev.Assets.transfer({
id: ASSET_ID,
target: carol.address,
amount: 1n,
})
.signed(signature({ sender: billy }))
.sent()
.dbgStatus("Billy Transfer Thawed Assets:")
.inBlockEvents()
.unhandleFailed()
.run(scope)

const carolBalance = await contractsDev.Assets.Account
.value(Rune.tuple([ASSET_ID, carol.publicKey]), contractsDev.latestBlockHash)
.unhandle(is(undefined))
.map(({ balance }) => balance)
.dbg("Carol Asset Balance:")
.run(scope)

assertEquals(carolBalance, 1n, "Carol Balance")

await contractsDev.Utility.batchAll({
calls: Rune.array([alexa.address, billy.address, carol.address])
.mapArray((addr) =>
contractsDev.Assets.burn({
id: ASSET_ID,
who: addr,
amount: 100_000_000_000_000n,
})
),
}).signed(signature({ sender: alexa }))
.sent()
.dbgStatus("Burn Everything:")
.inBlockEvents()
.run(scope)

await contractsDev.Assets.startDestroy({
id: ASSET_ID,
}).signed(signature({ sender: alexa }))
.sent()
.dbgStatus("Destroy Asset Class Start:")
.inBlockEvents()
.unhandleFailed()
.run(scope)

await contractsDev.Assets.finishDestroy({
id: ASSET_ID,
}).signed(signature({ sender: alexa }))
.sent()
.dbgStatus("Destroy Asset Class Finish:")
.inBlockEvents()
.unhandleFailed()
.run(scope)
208 changes: 208 additions & 0 deletions examples/dex.eg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import { localDev, NativeOrAssetId } from "@capi/local-dev"
import { alice, bob, is } from "capi"
import { signature } from "capi/patterns/signature/statemint"
import {
filterLiquidityAddedEvent,
filterPoolCreatedEvents,
getReserves,
quotePriceExactTokensForTokens,
} from "capi/patterns/unstable/dex"
import { Rune, Scope } from "../rune/Rune.ts"

const scope = new Scope()

const DOT_ASSET_ID = 0
const USDT_ASSET_ID = 1

const textEncoder = new TextEncoder()
const textDecoder = new TextDecoder()

await localDev.Assets.create({
id: DOT_ASSET_ID,
admin: alice.address,
minBalance: 1n,
}).signed(signature({ sender: alice }))
.sent()
.dbgStatus("Create DOT Asset:")
.finalizedEvents()
.unhandleFailed()
.run(scope)

await localDev.Assets.setMetadata({
id: DOT_ASSET_ID,
name: textEncoder.encode("Polkadot Token"),
symbol: textEncoder.encode("DOT"),
decimals: 2,
}).signed(signature({ sender: alice }))
.sent()
.dbgStatus("Set DOT Metadata:")
.finalizedEvents()
.unhandleFailed()
.run(scope)

await localDev.Assets.create({
id: USDT_ASSET_ID,
admin: alice.address,
minBalance: 1n,
}).signed(signature({ sender: alice }))
.sent()
.dbgStatus("Create USDT Asset:")
.finalizedEvents()
.unhandleFailed()
.run(scope)

await localDev.Assets.setMetadata({
id: USDT_ASSET_ID,
name: textEncoder.encode("United States Tether"),
symbol: textEncoder.encode("USDT"),
decimals: 2,
}).signed(signature({ sender: alice }))
.sent()
.dbgStatus("Set USDT Metadata:")
.finalizedEvents()
.unhandleFailed()
.run(scope)

const poolId = await localDev.AssetConversion.createPool({
asset1: NativeOrAssetId.Asset(DOT_ASSET_ID),
asset2: NativeOrAssetId.Asset(USDT_ASSET_ID),
}).signed(signature({ sender: alice }))
.sent()
.dbgStatus("Create DOT/USDT Pool:")
.finalizedEvents()
.unhandleFailed()
.pipe(filterPoolCreatedEvents)
.access(0, "poolId")
.run(scope)

await localDev.Assets.mint({
id: DOT_ASSET_ID,
beneficiary: alice.address,
amount: 218173n,
}).signed(signature({ sender: alice }))
.sent()
.dbgStatus("Mint DOT to Alice:")
.finalizedEvents()
.unhandleFailed()
.run(scope)

await localDev.Assets.mint({
id: USDT_ASSET_ID,
beneficiary: alice.address,
amount: 1000000n,
}).signed(signature({ sender: alice }))
.sent()
.dbgStatus("Mint USDT to Alice:")
.finalizedEvents()
.unhandleFailed()
.run(scope)

const lpTokenAmount = await localDev.AssetConversion.addLiquidity({
asset1: NativeOrAssetId.Asset(DOT_ASSET_ID),
asset2: NativeOrAssetId.Asset(USDT_ASSET_ID),
amount1Desired: 218173n,
amount2Desired: 1000000n,
amount1Min: 218173n,
amount2Min: 1000000n,
mintTo: alice.publicKey,
}).signed(signature({ sender: alice }))
.sent()
.dbgStatus("Add Liquidity to DOT/USDT Pool:")
.finalizedEvents()
.unhandleFailed()
.pipe(filterLiquidityAddedEvent)
.access(0, "lpTokenMinted")
.run(scope)

await localDev.Assets.mint({
id: USDT_ASSET_ID,
beneficiary: bob.address,
amount: 30000n,
}).signed(signature({ sender: alice }))
.sent()
.dbgStatus("Mint USDT to Bob:")
.finalizedEvents()
.unhandleFailed()
.run(scope)

await quotePriceExactTokensForTokens(
NativeOrAssetId.Asset(USDT_ASSET_ID),
NativeOrAssetId.Asset(DOT_ASSET_ID),
30000n,
true,
)
.dbg("DOT/USDT Quote:")
.run(scope)

await getReserves(NativeOrAssetId.Asset(USDT_ASSET_ID), NativeOrAssetId.Asset(DOT_ASSET_ID))
.dbg("DOT/USDT Reserves Before:")
.run(scope)

// swap with no slippage checks
await localDev.AssetConversion.swapExactTokensForTokens({
path: Rune.tuple([NativeOrAssetId.Asset(USDT_ASSET_ID), NativeOrAssetId.Asset(DOT_ASSET_ID)]),
amountIn: 30000n,
amountOutMin: 1n,
sendTo: bob.publicKey,
keepAlive: false,
}).signed(signature({ sender: bob }))
.sent()
.dbgStatus("Bob Swap 30k USDT for DOT:")
.finalizedEvents()
.unhandleFailed()
.run(scope)

const bobDotBalance = await localDev.Assets.Account
.value(Rune.tuple([DOT_ASSET_ID, bob.publicKey]))
.unhandle(is(undefined))
.map(({ balance }) => balance)
.dbg("Bob's Dot Balance:")
.run(scope)

await getReserves(NativeOrAssetId.Asset(USDT_ASSET_ID), NativeOrAssetId.Asset(DOT_ASSET_ID))
.dbg("DOT/USDT Reserves After:")
.run(scope)

await localDev.AssetConversion.removeLiquidity({
asset1: NativeOrAssetId.Asset(DOT_ASSET_ID),
asset2: NativeOrAssetId.Asset(USDT_ASSET_ID),
lpTokenBurn: lpTokenAmount / 2n,
amount1MinReceive: 1n,
amount2MinReceive: 1n,
withdrawTo: alice.publicKey,
}).signed(signature({ sender: alice }))
.sent()
.dbgStatus("Remove Half Liquidity to DOT/USDT Pool:")
.finalizedEvents()
.unhandleFailed()
.run(scope)

await quotePriceExactTokensForTokens(
NativeOrAssetId.Asset(DOT_ASSET_ID),
NativeOrAssetId.Asset(USDT_ASSET_ID),
bobDotBalance,
true,
)
.dbg("USDT/DOT Quote:")
.run(scope)

// Should not get back 30k - fees since liquidity was reduced
await localDev.AssetConversion.swapExactTokensForTokens({
path: Rune.tuple([NativeOrAssetId.Asset(DOT_ASSET_ID), NativeOrAssetId.Asset(USDT_ASSET_ID)]),
amountIn: bobDotBalance,
amountOutMin: 1n,
sendTo: bob.publicKey,
keepAlive: false,
}).signed(signature({ sender: bob }))
.sent()
.dbgStatus(`Bob Swap ${bobDotBalance} USDT for DOT:`)
.finalizedEvents()
.unhandleFailed()
.run(scope)

await localDev.Assets.Account
.value(Rune.tuple([USDT_ASSET_ID, bob.publicKey]))
.unhandle(is(undefined))
.map(({ balance }) => balance)
.dbg("Bob's USDT Balance:")
.run(scope)
Loading