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

feat: add reserve transfer example #765

Merged
merged 39 commits into from
Apr 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
5868dfe
feat: add reserve transfer example
kratico Mar 20, 2023
7266c21
feat: add xcm_reserve_transfer example
kratico Mar 21, 2023
dc3405f
feat: filter XcmpMessageSent.messageHash
kratico Mar 22, 2023
eb73459
feat: refactor example
kratico Mar 22, 2023
191b376
feat: remove xcm::double_encoded::DoubleEncoded override
kratico Mar 23, 2023
21a269a
feat: use capi-binary-builds
kratico Mar 28, 2023
0929037
rever: patterns/signature/polkadot.ts
kratico Mar 28, 2023
f270368
chore: remove todo comments
kratico Mar 28, 2023
7e0bebc
chore: move xcm/reserve_transfer.ts example
kratico Mar 31, 2023
fe2e96c
chore: rename reserve_transfer.eg.ts
kratico Apr 3, 2023
dc76792
some tweaks (#851)
harrysolovay Apr 5, 2023
0771a5f
ExtrinsicRune cleanup
harrysolovay Apr 6, 2023
842ce0a
ExtrinsicRune cleanup
harrysolovay Apr 6, 2023
b7c0298
resolve conflicts
harrysolovay Apr 6, 2023
c23796c
feat: add rococo-dev-xcm network
kratico Apr 10, 2023
7b81406
feat: add xcm hrmp channels to devnets
kratico Apr 10, 2023
cd749d5
chore: remove zombienets/
kratico Apr 10, 2023
285233a
chore: update words.txt
kratico Apr 10, 2023
aba098a
feat: update reserve asset example types
kratico Apr 11, 2023
2cf0ec4
feat: update trappist-collator version
kratico Apr 11, 2023
e70a6a5
Apply suggestions from code review
kratico Apr 11, 2023
e8d71af
feat: update addXcmHrmpChannels
kratico Apr 11, 2023
c927cf0
feat: extract hrmpChannelMaxCapacity hrmpChannelMaxMessageSize into c…
kratico Apr 11, 2023
54cc195
Update words.txt
kratico Apr 12, 2023
40a07b4
pairing with matias
harrysolovay Apr 12, 2023
f92b293
feat: inline billyTrappistBalance
kratico Apr 12, 2023
07ca270
feat: use alice for sudo calls
kratico Apr 12, 2023
400063c
fix: ingnore smoldot example
kratico Apr 12, 2023
76045d7
chore: re-enable smoldot example
kratico Apr 14, 2023
6569aa2
feat: add sovereign account codec
kratico Apr 14, 2023
bd78ffb
chore: remove extra comments
kratico Apr 14, 2023
dcd6f96
rebase and use factories
harrysolovay Apr 15, 2023
0de6835
rebase and fix type errors
harrysolovay Apr 15, 2023
0d2e6ab
feat: update import_map.json
kratico Apr 17, 2023
bcb5b6d
revert: patterns/signature/polkadot.ts
kratico Apr 17, 2023
4b4b150
Apply suggestions from code review
kratico Apr 18, 2023
7938a4f
fix: GH commit suggestions
kratico Apr 18, 2023
0423523
feat: remove signature/westmint.ts
kratico Apr 18, 2023
75785f9
feat: rename $call to $callData
kratico Apr 18, 2023
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
2 changes: 1 addition & 1 deletion .trunignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
examples/xcm/*.eg.ts
tjjfvi marked this conversation as resolved.
Show resolved Hide resolved
examples/ink/*.eg.ts
examples/nfts
examples/xcm/asset_teleportation.eg.ts
16 changes: 16 additions & 0 deletions capi.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,21 @@ export const config: CapiConfig = {
},
},
},
rococoDevXcm: {
tjjfvi marked this conversation as resolved.
Show resolved Hide resolved
binary: binary("polkadot", "v0.9.37"),
chain: "rococo-local",
parachains: {
statemine: {
id: 1000,
binary: binary("polkadot-parachain", "v0.9.370"),
chain: "statemine-local",
},
trappist: {
id: 2000,
binary: binary("trappist-collator", "79bba6e"),
chain: "local",
},
},
},
},
}
8 changes: 8 additions & 0 deletions devnets/chainSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,12 @@ export interface GenesisConfig {
session?: {
keys: [account: string, account: string, key: SessionKey][]
}
hrmp?: {
preopenHrmpChannels: [
senderParaId: number,
recipientParaId: number,
maxCapacity: number,
maxMessageSize: number,
][]
}
}
26 changes: 25 additions & 1 deletion devnets/startNetwork.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export async function startNetwork(
genesisConfig.paras.paras.push(
...paras.map(({ id, genesis }) => [id, [...genesis, true]] satisfies Narrow),
)
addXcmHrmpChannels(genesisConfig, paras.map(({ id }) => id))
}
addAuthorities(genesisConfig, minValidators)
addTestUsers(genesisConfig.balances.balances)
Expand All @@ -74,6 +75,7 @@ export async function startNetwork(
relaySpec,
config.nodes ?? minValidators,
[],
relayBinary,
signal,
)
return {
Expand All @@ -95,6 +97,7 @@ export async function startNetwork(
"--bootnodes",
relay.bootnodes,
],
relayBinary,
signal,
)
return [name, chain] satisfies Narrow
Expand Down Expand Up @@ -147,6 +150,7 @@ async function spawnChain(
chain: string,
count: number,
extraArgs: string[],
generateNodeKeyBinary: string,
signal: AbortSignal,
): Promise<NetworkChain> {
let bootnodes: string | undefined
Expand Down Expand Up @@ -175,7 +179,7 @@ async function spawnChain(
if (bootnodes) {
args.push("--bootnodes", bootnodes)
} else {
const { nodeKey, peerId } = await generateNodeKey(binary)
const { nodeKey, peerId } = await generateNodeKey(generateNodeKeyBinary)
tjjfvi marked this conversation as resolved.
Show resolved Hide resolved
args.push("--node-key", nodeKey)
bootnodes = generateBootnodeString(httpPort, peerId)
}
Expand Down Expand Up @@ -288,3 +292,23 @@ function addAuthorities(genesisConfig: GenesisConfig, count: number) {
])
)
}

const hrmpChannelMaxCapacity = 8
const hrmpChannelMaxMessageSize = 512
function addXcmHrmpChannels(
genesisConfig: GenesisConfig,
paraIds: number[],
) {
genesisConfig.hrmp ??= { preopenHrmpChannels: [] }
for (const senderParaId of paraIds) {
for (const recipientParaId of paraIds) {
if (senderParaId === recipientParaId) continue
genesisConfig.hrmp.preopenHrmpChannels.push([
tjjfvi marked this conversation as resolved.
Show resolved Hide resolved
senderParaId,
recipientParaId,
hrmpChannelMaxCapacity,
hrmpChannelMaxMessageSize,
])
}
}
}
6 changes: 3 additions & 3 deletions examples/multisig/basic.eg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,21 +57,21 @@ await multisig
.run()

// Check whether the call has been proposed.
const isProposed = await multisig.isProposed(call.hash).run()
const isProposed = await multisig.isProposed(call.callHash).run()
console.log("Is proposed:", isProposed)
assert(isProposed)

// Approve proposal as Billy.
await multisig // TODO: get `ratify` working in place of `approve`
.approve({ callHash: call.hash, sender: billy.address })
.approve({ callHash: call.callHash, sender: billy.address })
.signed(signature({ sender: billy }))
.sent()
.dbgStatus("First approval:")
.finalized()
.run()

const { approvals } = await multisig
.proposal(call.hash)
.proposal(call.callHash)
.unhandle(undefined)
.run()

Expand Down
2 changes: 1 addition & 1 deletion examples/nfts.eg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
import { assertEquals } from "asserts"
import { $, Rune } from "capi"
import { DefaultCollectionSetting, DefaultItemSetting } from "capi/patterns/nfts.ts"
import { signature } from "capi/patterns/signature/westmint.ts"
import { signature } from "capi/patterns/signature/statemint.ts"

// Create two test users. Alexa will mint and list the NFT. Billy will purchase it.
const { alexa, billy } = await createUsers()
Expand Down
218 changes: 218 additions & 0 deletions examples/xcm/reserve_transfer.eg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
/**
* @title XCM Reserve Asset Transfer
* @stability unstable
* @description Perform an XCM reserve asset transfer, in which two chains, which
* do not trust one another, rely on a third chain to store assets and facilitate
* the exchange.
*/

import * as Rococo from "@capi/rococo-dev-xcm"
import * as Statemine from "@capi/rococo-dev-xcm/statemine"
import * as Trappist from "@capi/rococo-dev-xcm/trappist"
import { assert, assertNotEquals } from "asserts"
import { $, alice as root, Rune } from "capi"
import { $siblId } from "capi/patterns/para_id.ts"
import { signature } from "capi/patterns/signature/statemint.ts"
import { retry } from "../../deps/std/async.ts"

const { alexa, billy } = await Statemine.createUsers()

// Define some constants for later use.
const RESERVE_ASSET_ID = 1
const RESERVE_CHAIN_ID = 1000 // Statemine
const TRAPPIST_ASSET_ID = RESERVE_ASSET_ID
const TRAPPIST_CHAIN_ID = 2000

// Define some common options to be used along with `retry`,
// which will poll for XCM-resulting changes.
const retryOptions = {
multiplier: 1,
maxAttempts: Infinity,
maxTimeout: 2 * 60 * 1000,
}

// Create a sufficient asset with Sudo. When targeting a common good
// parachain, access root instead through the relay chain.
await Rococo.Sudo
.sudo({
call: Rococo.ParasSudoWrapper.sudoQueueDownwardXcm({
id: RESERVE_CHAIN_ID,
xcm: Rococo.VersionedXcm.V2(
Rune.array([
Rococo.Instruction.Transact({
originType: "Superuser",
requireWeightAtMost: 1000000000n,
call: Rococo.DoubleEncoded({
encoded: Statemine.Assets.forceCreate({
id: RESERVE_ASSET_ID,
isSufficient: true,
minBalance: 1n,
owner: alexa.address,
}).callData,
}),
}),
]),
),
}),
})
.signed(signature({ sender: root }))
.sent()
.dbgStatus("Rococo(root) > Statemine(root): Create asset")
.finalized()
.run()

// Wait for the asset to be recorded in storage.
const assetDetails = await retry(
() => Statemine.Assets.Asset.value(RESERVE_ASSET_ID).unhandle(undefined).run(),
retryOptions,
)

// Ensure the reserve asset was created.
console.log("Statemine: Asset created", assetDetails)
$.assert(Statemine.$assetDetails, assetDetails)

// Mint assets on reserve parachain.
await Statemine.Assets
.mint({
id: RESERVE_ASSET_ID,
amount: 100000000000000n,
beneficiary: billy.address,
})
.signed(signature({ sender: alexa }))
.sent()
.dbgStatus("Statemine(Alexa): Mint reserve asset to Billy")
.finalized()
.run()

const billyStatemintBalance = Statemine.Assets.Account
.value([RESERVE_ASSET_ID, billy.publicKey])
.unhandle(undefined)
.access("balance")

const billyStatemintBalanceInitial = await billyStatemintBalance.run()
console.log("Statemine(Billy): asset balance", billyStatemintBalanceInitial)
$.assert($.u128, billyStatemintBalanceInitial)

// Create the asset on the Trappist parachain.
await Trappist.Sudo
.sudo({
call: Trappist.Assets.forceCreate({
id: TRAPPIST_ASSET_ID,
isSufficient: false,
minBalance: 1n,
owner: alexa.address,
}),
})
.signed(signature({ sender: root }))
.sent()
.dbgStatus("Trappist(root): Create derived asset")
.finalized()
.run()

// Register Trappist parachain asset id to reserve asset id.
await Trappist.Sudo
.sudo({
call: Trappist.AssetRegistry.registerReserveAsset({
assetId: TRAPPIST_ASSET_ID,
assetMultiLocation: Trappist.XcmV1MultiLocation({
parents: 1,
interior: Trappist.Junctions.X3(
Trappist.XcmV1Junction.Parachain(RESERVE_CHAIN_ID),
Trappist.XcmV1Junction.PalletInstance((await Statemine.Assets.pallet.run()).id),
Trappist.XcmV1Junction.GeneralIndex(BigInt(RESERVE_ASSET_ID)),
),
}),
}),
})
.signed(signature({ sender: root }))
.sent()
.dbgStatus("Trappist(root): Register AssetId to Reserve AssetId")
.finalized()
.run()

// Reserve transfer asset id on reserve parachain to Trappist parachain.
{
// Destructure the factories to be used (for convenience).
const {
VersionedMultiLocation,
VersionedMultiAssets,
Fungibility,
XcmV1Junction,
Junctions,
AssetId,
NetworkId,
WeightLimit,
RuntimeEvent,
CumulusPalletXcmpQueueEvent: { isXcmpMessageSent },
} = Statemine
const events = await Statemine.PolkadotXcm
.limitedReserveTransferAssets({
dest: VersionedMultiLocation.V1(Statemine.XcmV1MultiLocation({
parents: 1,
interior: Junctions.X1(
XcmV1Junction.Parachain(TRAPPIST_CHAIN_ID),
),
})),
beneficiary: VersionedMultiLocation.V1(Statemine.XcmV1MultiLocation({
parents: 0,
interior: Junctions.X1(
XcmV1Junction.AccountId32({
network: NetworkId.Any(),
id: billy.publicKey,
}),
),
})),
assets: VersionedMultiAssets.V1(Rune.array([Statemine.XcmV1MultiAsset({
id: AssetId.Concrete(Statemine.XcmV1MultiLocation({
parents: 0,
interior: Junctions.X2(
XcmV1Junction.PalletInstance((await Statemine.Assets.pallet.run()).id),
XcmV1Junction.GeneralIndex(BigInt(RESERVE_ASSET_ID)),
),
})),
fun: Fungibility.Fungible(10000000000000n),
})])),
feeAssetItem: 0,
weightLimit: WeightLimit.Unlimited(),
})
.signed(signature({ sender: billy }))
.sent()
.dbgStatus("Statemine(Billy): Reserve transfer to Trappist")
.finalizedEvents()
.run()

for (const { event } of events) {
if (RuntimeEvent.isXcmpQueue(event) && isXcmpMessageSent(event.value)) {
console.log("XcmpMessageSent.messageHash:", event.value)
}
}
}

// Retrieve billy's balance on Trappist.
const { balance: billyTrappistBalance } = await retry(
() =>
Trappist.Assets.Account.value([TRAPPIST_ASSET_ID, billy.publicKey]).unhandle(undefined).run(),
retryOptions,
)

// Ensure the balance is greater than zero.
console.log("Trappist(Billy): asset balance:", billyTrappistBalance)
assert(billyTrappistBalance > 0)

// Retrieve Billy's balance on statemint.
const billyStatemintBalanceFinal = await billyStatemintBalance.run()

// Ensure the balance is different from the initial.
console.log("Statemine(Billy): asset balance:", billyStatemintBalanceFinal)
assertNotEquals(billyStatemintBalanceInitial, billyStatemintBalanceFinal)

// Retrieve the statemint sovereign account balance.
const statemintSovereignAccountBalance = await Statemine.Assets.Account
.value([RESERVE_ASSET_ID, $siblId.encode(TRAPPIST_CHAIN_ID)])
.unhandle(undefined)
.access("balance")
.run()

// Ensure the balance is greater than zero.
console.log("Statemine(TrappistSovereignAccount): asset balance", statemintSovereignAccountBalance)
assert(statemintSovereignAccountBalance > 0)
2 changes: 1 addition & 1 deletion fluent/ChainRune.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export class ChainRune<out C extends Chain, out U> extends Rune<C, U> {
.into(PalletRune, this.as(ChainRune))
}

addressPrefix(this: ChainRune<AddressPrefixChain, U>) {
addressPrefix() {
return this
.pallet("System")
.constant("SS58Prefix")
Expand Down
Loading