Skip to content
This repository has been 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
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