From 59e44fea18a580115f16d36284d5e090b4015f46 Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 18 May 2026 21:49:48 +0800 Subject: [PATCH 1/5] add atomic extension check --- contract-tests/test/wasm.contract.test.ts | 55 ++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/contract-tests/test/wasm.contract.test.ts b/contract-tests/test/wasm.contract.test.ts index cd78c8d942..9ad203ae36 100644 --- a/contract-tests/test/wasm.contract.test.ts +++ b/contract-tests/test/wasm.contract.test.ts @@ -7,7 +7,7 @@ import { Binary, TypedApi } from "polkadot-api"; import { contracts } from "../.papi/descriptors"; import { convertPublicKeyToSs58 } from "../src/address-utils"; import { tao } from "../src/balance-math"; -import { getDevnetApi, getRandomSubstrateKeypair, getSignerFromKeypair, waitForTransactionWithRetry } from "../src/substrate"; +import { getBalance, getDevnetApi, getRandomSubstrateKeypair, getSignerFromKeypair, waitForTransactionWithRetry } from "../src/substrate"; import { addNewSubnetwork, burnedRegister, forceSetBalanceToSs58Address, sendWasmContractExtrinsic, setAdminFreezeWindow, setTargetRegistrationsPerInterval, startCall } from "../src/subtensor"; const bittensorWasmPath = "./bittensor/target/ink/bittensor.wasm" @@ -71,6 +71,17 @@ describe("Test wasm contract", () => { return stake as bigint } + async function getContractStakeOnRoot(): Promise { + const stake = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + contractAddress, + 0, + ))?.stake + + assert.ok(stake !== undefined) + return stake as bigint + } + async function initSecondColdAndHotkey() { hotkey2 = getRandomSubstrateKeypair(); coldkey2 = getRandomSubstrateKeypair(); @@ -920,4 +931,46 @@ describe("Test wasm contract", () => { proxies = await api.query.Proxy.Proxies.getValue(convertPublicKeyToSs58(coldkey.publicKey)) assert.ok(proxies !== undefined && proxies[0].length === 0) }) + + it("Check add_stake_recycle is atomic operation", async () => { + const stakeBefore = await getContractStakeOnRoot() + const balanceBefore = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)) + + // recycle alpha on root subnet is not allowed, the extrinsic should be failed. + const message = inkClient.message("add_stake_recycle") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid: 0, + amount: tao(100), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + + const stakeAfter = await getContractStakeOnRoot() + const balanceAfter = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)) + + assert.ok(balanceBefore - balanceAfter < 1_000_000) + assert.equal(stakeAfter, stakeBefore) + }) + + it("Check add_stake_burn is atomic operation", async () => { + const stakeBefore = await getContractStakeOnRoot() + const balanceBefore = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)) + const alphaOutBefore = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) + + const message = inkClient.message("add_stake_burn") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid: 0, + amount: tao(100), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + + const stakeAfter = await getContractStakeOnRoot() + const alphaOutAfter = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) + const balanceAfter = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)) + + assert.ok(balanceBefore - balanceAfter < 1_000_000) + assert.equal(stakeAfter, stakeBefore) + assert.ok(alphaOutAfter > alphaOutBefore) + }) }); \ No newline at end of file From d61e024db54d48d7bb80dbf0f5b91e28c1c9ac51 Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 18 May 2026 23:26:09 +0800 Subject: [PATCH 2/5] need more transaction fee --- contract-tests/test/wasm.contract.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract-tests/test/wasm.contract.test.ts b/contract-tests/test/wasm.contract.test.ts index 9ad203ae36..0251bf4707 100644 --- a/contract-tests/test/wasm.contract.test.ts +++ b/contract-tests/test/wasm.contract.test.ts @@ -948,7 +948,7 @@ describe("Test wasm contract", () => { const stakeAfter = await getContractStakeOnRoot() const balanceAfter = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)) - assert.ok(balanceBefore - balanceAfter < 1_000_000) + assert.ok(balanceBefore - balanceAfter < 10_000_000) assert.equal(stakeAfter, stakeBefore) }) From 481043492d0395cdd0eb4938c3e3d35169a8c0ca Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 19 May 2026 07:41:35 +0800 Subject: [PATCH 3/5] give more transaction fee --- contract-tests/test/wasm.contract.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contract-tests/test/wasm.contract.test.ts b/contract-tests/test/wasm.contract.test.ts index 0251bf4707..6ae8d82c08 100644 --- a/contract-tests/test/wasm.contract.test.ts +++ b/contract-tests/test/wasm.contract.test.ts @@ -969,7 +969,7 @@ describe("Test wasm contract", () => { const alphaOutAfter = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) const balanceAfter = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)) - assert.ok(balanceBefore - balanceAfter < 1_000_000) + assert.ok(balanceBefore - balanceAfter < 10_000_000) assert.equal(stakeAfter, stakeBefore) assert.ok(alphaOutAfter > alphaOutBefore) }) From 0e09e67819470e0dc9c6b1a7baac5b2033775e83 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 20 May 2026 07:31:52 +0800 Subject: [PATCH 4/5] only run one test --- contract-tests/bittensor/lib.rs | 29 + contract-tests/package.json | 2 +- contract-tests/test/wasm.contract.test.ts | 837 +--------------------- 3 files changed, 58 insertions(+), 810 deletions(-) diff --git a/contract-tests/bittensor/lib.rs b/contract-tests/bittensor/lib.rs index bff61d3b08..85452f058c 100755 --- a/contract-tests/bittensor/lib.rs +++ b/contract-tests/bittensor/lib.rs @@ -801,5 +801,34 @@ mod bittensor { .caller_remove_proxy(delegate.into()) .map_err(|_e| ReadWriteErrorCode::WriteFailed) } + + #[ink(message)] + pub fn add_stake_recycle_no_revert( + &self, + hotkey: [u8; 32], + netuid: u16, + amount: u64, + ) -> u64 { + match self + .env() + .extension() + .add_stake_recycle(hotkey.into(), netuid, amount) + { + Ok(real_amount) => real_amount, + Err(_) => 0, + } + } + + #[ink(message)] + pub fn add_stake_burn_no_revert(&self, hotkey: [u8; 32], netuid: u16, amount: u64) -> u64 { + match self + .env() + .extension() + .add_stake_burn(hotkey.into(), netuid, amount) + { + Ok(real_amount) => real_amount, + Err(_) => 0, + } + } } } diff --git a/contract-tests/package.json b/contract-tests/package.json index 3acf069c1d..6b5eda8146 100644 --- a/contract-tests/package.json +++ b/contract-tests/package.json @@ -1,6 +1,6 @@ { "scripts": { - "test": "TS_NODE_PREFER_TS_EXTS=1 TS_NODE_TRANSPILE_ONLY=1 mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register --extension ts \"test/**/*.ts\"" + "test": "TS_NODE_PREFER_TS_EXTS=1 TS_NODE_TRANSPILE_ONLY=1 mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register --extension ts \"test/wasm.contract.test.ts\"" }, "keywords": [], "author": "", diff --git a/contract-tests/test/wasm.contract.test.ts b/contract-tests/test/wasm.contract.test.ts index 6ae8d82c08..a8e88b086a 100644 --- a/contract-tests/test/wasm.contract.test.ts +++ b/contract-tests/test/wasm.contract.test.ts @@ -148,829 +148,48 @@ describe("Test wasm contract", () => { await waitForTransactionWithRetry(api, transfer, signer) }) - - it("Can query stake info from contract", async () => { - - const queryMessage = inkClient.message("get_stake_info_for_hotkey_coldkey_netuid") - - const data = queryMessage.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - coldkey: Binary.fromBytes(coldkey.publicKey), - netuid: netuid, - }) - - const response = await api.apis.ContractsApi.call( - convertPublicKeyToSs58(hotkey.publicKey), - contractAddress, - BigInt(0), - undefined, - undefined, - Binary.fromBytes(data.asBytes()), - ) - - assert.ok(response.result.success) - const result = queryMessage.decode(response.result.value).value.value - - if (typeof result === "object" && "hotkey" in result && "coldkey" in result && "netuid" in result && "stake" in result && "locked" in result && "emission" in result && "tao_emission" in result && "drain" in result && "is_registered" in result) { - assert.equal(result.hotkey, convertPublicKeyToSs58(hotkey.publicKey)) - assert.equal(result.coldkey, convertPublicKeyToSs58(coldkey.publicKey)) - assert.equal(result.netuid, netuid) - assert.equal(result.is_registered, true) - } else { - throw new Error("result is not an object") - } - - }) - - it("Can add stake to contract", async () => { - await addStakeViaContract(true) - }) - - it("Can remove stake to contract", async () => { - await addStakeViaContract(true) - const stake = await getContractStake() - - let amount = stake / BigInt(2) - const message = inkClient.message("remove_stake") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid: netuid, - amount: amount, - }) - - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stakeAfterAddStake = await getContractStake() - - assert.ok(stakeAfterAddStake < stake) - }) - - it("Can unstake all from contract", async () => { - await addStakeViaContract(true) - // Get stake before unstake_all - const stakeBefore = await getContractStake() - - assert.ok(stakeBefore > BigInt(0)) - - // Call unstake_all - const unstakeMessage = inkClient.message("unstake_all") - const unstakeData = unstakeMessage.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, unstakeData) - - // Verify stake is now zero - const stakeAfter = await getContractStake() - - assert.equal(stakeAfter, BigInt(0)) - }) - - it("Can unstake all alpha from contract", async () => { - await addStakeViaContract(true) - // Get stake before unstake_all_alpha - const stakeBefore = await getContractStake() - - assert.ok(stakeBefore > BigInt(0)) - - // Call unstake_all_alpha - const message = inkClient.message("unstake_all_alpha") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - // Verify stake is now zero - const stakeAfter = await getContractStake() - - assert.equal(stakeAfter, BigInt(0)) - }) - - it("Can move stake between hotkeys", async () => { - await addStakeViaContract(true) - await initSecondColdAndHotkey() - // Get initial stakes - const originStakeBefore = await getContractStake() - - const destStakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey2.publicKey), - contractAddress, - netuid, - ))?.stake || BigInt(0) - - assert.ok(originStakeBefore > BigInt(0)) - - // Move stake - const moveAmount = originStakeBefore / BigInt(2) - const message = inkClient.message("move_stake") - const data = message.encode({ - origin_hotkey: Binary.fromBytes(hotkey.publicKey), - destination_hotkey: Binary.fromBytes(hotkey2.publicKey), - origin_netuid: netuid, - destination_netuid: netuid, - amount: moveAmount, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - // Verify stakes changed - const originStakeAfter = await getContractStake() - - const destStakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey2.publicKey), - contractAddress, - netuid, - ))?.stake - - assert.ok(destStakeAfter !== undefined) - assert.ok(originStakeAfter < originStakeBefore) - assert.ok(destStakeAfter > destStakeBefore) - }) - - it("Can transfer stake between coldkeys", async () => { - await addStakeViaContract(true) - await initSecondColdAndHotkey() - // Get initial stake - const stakeBeforeOrigin = await getContractStake() - - const stakeBeforeDest = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey2.publicKey), - netuid, - ))?.stake - - assert.ok(stakeBeforeOrigin > BigInt(0)) - assert.ok(stakeBeforeDest !== undefined) - - // Transfer stake - const transferAmount = stakeBeforeOrigin / BigInt(2) - const message = inkClient.message("transfer_stake") - const data = message.encode({ - destination_coldkey: Binary.fromBytes(coldkey2.publicKey), - hotkey: Binary.fromBytes(hotkey.publicKey), - origin_netuid: netuid, - destination_netuid: netuid, - amount: transferAmount, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - // Verify stake transferred - const stakeAfterOrigin = await getContractStake() - - const stakeAfterDest = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey2.publicKey), - netuid, - ))?.stake - - assert.ok(stakeAfterDest !== undefined) - assert.ok(stakeAfterOrigin < stakeBeforeOrigin) - assert.ok(stakeAfterDest > stakeBeforeDest!) - }) - - it("Can swap stake between networks", async () => { - await addStakeViaContract(true) - // Get initial stakes - const stakeBefore = await getContractStake() - - const stakeBefore2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - contractAddress, - netuid + 1, - ))?.stake || BigInt(0) - - assert.ok(stakeBefore > BigInt(0)) - - // Swap stake - const swapAmount = stakeBefore / BigInt(2) - const message = inkClient.message("swap_stake") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - origin_netuid: netuid, - destination_netuid: netuid + 1, - amount: swapAmount, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - // Verify stakes swapped - const stakeAfter = await getContractStake() - - const stakeAfter2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - contractAddress, - netuid + 1, - ))?.stake - - assert.ok(stakeAfter2 !== undefined) - assert.ok(stakeAfter < stakeBefore) - assert.ok(stakeAfter2 > stakeBefore2) - }) - - it("Can add stake with limit", async () => { - const stakeBefore = await getContractStake() - - const message = inkClient.message("add_stake_limit") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid: netuid, - amount: tao(200), - limit_price: tao(100), - allow_partial: false, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - // Verify stake was added - const stakeAfter = await getContractStake() - - assert.ok(stakeAfter > stakeBefore) - }) - - it("Can remove stake with limit", async () => { - await addStakeViaContract(true) - const stakeBefore = await getContractStake() - - assert.ok(stakeBefore > BigInt(0)) - - const message = inkClient.message("remove_stake_limit") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid: netuid, - amount: stakeBefore / BigInt(2), - limit_price: tao(1), - allow_partial: false, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stakeAfter = await getContractStake() - - assert.ok(stakeAfter < stakeBefore) - }) - - it("Can swap stake with limit", async () => { - await addStakeViaContract(true) - - const stakeBefore = await getContractStake() - - const stakeBefore2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - contractAddress, - netuid + 1, - ))?.stake - - assert.ok(stakeBefore > BigInt(0)) - assert.ok(stakeBefore2 !== undefined) - - const message = inkClient.message("swap_stake_limit") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - origin_netuid: netuid, - destination_netuid: netuid + 1, - amount: stakeBefore / BigInt(2), - limit_price: tao(1), - allow_partial: false, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stakeAfter = await getContractStake() - - const stakeAfter2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - contractAddress, - netuid + 1, - ))?.stake - - assert.ok(stakeAfter2 !== undefined) - assert.ok(stakeAfter < stakeBefore) - assert.ok(stakeAfter2 > stakeBefore2) - }) - - it("Can remove stake full limit", async () => { - await addStakeViaContract(true) - const stakeBefore = await getContractStake() - - assert.ok(stakeBefore > BigInt(0)) - - const message = inkClient.message("remove_stake_full_limit") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid: netuid, - limit_price: undefined, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stakeAfter = await getContractStake() - - assert.ok(stakeAfter < stakeBefore) - }) - - it("Can set coldkey auto stake hotkey", async () => { - const message = inkClient.message("set_coldkey_auto_stake_hotkey") - const data = message.encode({ - netuid: netuid, - hotkey: Binary.fromBytes(hotkey.publicKey), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - let autoStakeHotkey = await api.query.SubtensorModule.AutoStakeDestination.getValue( - contractAddress, - netuid, - ) - - assert.ok(autoStakeHotkey !== undefined) - assert.ok(autoStakeHotkey === convertPublicKeyToSs58(hotkey.publicKey)) - }) - - it("Can add and remove proxy", async () => { - const message = inkClient.message("add_proxy") - const data = message.encode({ - delegate: Binary.fromBytes(hotkey.publicKey), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - let proxies = await api.query.Proxy.Proxies.getValue( - contractAddress, - ) - assert.ok(proxies !== undefined) - assert.ok(proxies.length > 0 && proxies[0].length > 0) - assert.ok(proxies[0][0].delegate === convertPublicKeyToSs58(hotkey.publicKey)) - - - const removeMessage = inkClient.message("remove_proxy") - const removeData = removeMessage.encode({ - delegate: Binary.fromBytes(hotkey.publicKey), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, removeData) - - let proxiesAfterRemove = await api.query.Proxy.Proxies.getValue( - contractAddress, - ) - assert.ok(proxiesAfterRemove !== undefined) - assert.ok(proxiesAfterRemove[0].length === 0) - }) - - it("Can get alpha price", async () => { - const message = inkClient.message("get_alpha_price") - const data = message.encode({ - netuid: netuid, - }) - - const response = await api.apis.ContractsApi.call( - convertPublicKeyToSs58(hotkey.publicKey), - contractAddress, - BigInt(0), - undefined, - undefined, - Binary.fromBytes(data.asBytes()), - ) - - assert.ok(response.result.success) - const result = message.decode(response.result.value).value.value - - assert.ok(result !== undefined) - }) - - it("Can recycle alpha from contract stake", async () => { - await addStakeViaContract(true) - const stakeBefore = await getContractStake() - const alphaOutBefore = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) - - const message = inkClient.message("recycle_alpha") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid, - amount: stakeBefore / BigInt(2), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stakeAfter = await getContractStake() - const alphaOutAfter = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) - - assert.ok(stakeAfter < stakeBefore) - assert.ok(alphaOutAfter < alphaOutBefore) - }) - - it("Can burn alpha from contract stake", async () => { - await addStakeViaContract(true) - const stakeBefore = await getContractStake() - const alphaBurnedBefore = await api.query.AlphaAssets.AlphaBurned.getValue(netuid) - - const message = inkClient.message("burn_alpha") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid, - amount: stakeBefore / BigInt(2), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stakeAfter = await getContractStake() - const alphaBurnedAfter = await api.query.AlphaAssets.AlphaBurned.getValue(netuid) - - assert.ok(stakeAfter < stakeBefore) - assert.ok(alphaBurnedBefore < alphaBurnedAfter) - }) - - it("Can add stake and recycle resulting alpha", async () => { - const stakeBefore = await getContractStake() - - const message = inkClient.message("add_stake_recycle") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid, - amount: tao(100), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stakeAfter = await getContractStake() - - assert.equal(stakeAfter, stakeBefore) - }) - - it("Can add stake and burn resulting alpha", async () => { - const stakeBefore = await getContractStake() - const alphaOutBefore = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) - - const message = inkClient.message("add_stake_burn") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid, - amount: tao(100), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stakeAfter = await getContractStake() - const alphaOutAfter = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) - - assert.equal(stakeAfter, stakeBefore) - assert.ok(alphaOutAfter > alphaOutBefore) - }) - - it("Can caller add stake (fn 20)", async () => { - await addStakeViaContract(false) - }) - - it("Can caller remove stake (fn 21)", async () => { - await addStakeViaContract(false) - const stake = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stake !== undefined) - const amount = stake / BigInt(2) - const message = inkClient.message("caller_remove_stake") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid, - amount, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeAfter !== undefined && stakeAfter < stake!) - }) - - it("Can caller unstake_all (fn 22)", async () => { - await addStakeViaContract(false) - const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) - const message = inkClient.message("caller_unstake_all") - const data = message.encode({ hotkey: Binary.fromBytes(hotkey.publicKey) }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeAfter !== undefined) - assert.ok(stakeAfter < stakeBefore!) - }) - - it("Can caller unstake_all_alpha (fn 23)", async () => { - await addStakeViaContract(false) - const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) - const message = inkClient.message("caller_unstake_all_alpha") - const data = message.encode({ hotkey: Binary.fromBytes(hotkey.publicKey) }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeAfter !== undefined) - assert.ok(stakeAfter < stakeBefore!) - }) - - it("Can caller move_stake (fn 24)", async () => { - await addStakeViaContract(false) - await initSecondColdAndHotkey() - const originStakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - const destStakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey2.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake || BigInt(0) - assert.ok(originStakeBefore !== undefined && originStakeBefore > BigInt(0)) - const moveAmount = originStakeBefore / BigInt(2) - const message = inkClient.message("caller_move_stake") - const data = message.encode({ - origin_hotkey: Binary.fromBytes(hotkey.publicKey), - destination_hotkey: Binary.fromBytes(hotkey2.publicKey), - origin_netuid: netuid, - destination_netuid: netuid, - amount: moveAmount, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const originStakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - const destStakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey2.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(originStakeAfter !== undefined && destStakeAfter !== undefined) - assert.ok(originStakeAfter < originStakeBefore!) - assert.ok(destStakeAfter > destStakeBefore) - }) - - it("Can caller transfer_stake (fn 25)", async () => { - await addStakeViaContract(false) - await initSecondColdAndHotkey() - const stakeBeforeOrigin = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - const stakeBeforeDest = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey2.publicKey), - netuid, - ))?.stake - assert.ok(stakeBeforeOrigin !== undefined && stakeBeforeOrigin > BigInt(0)) - assert.ok(stakeBeforeDest !== undefined) - const transferAmount = stakeBeforeOrigin / BigInt(2) - const message = inkClient.message("caller_transfer_stake") - const data = message.encode({ - destination_coldkey: Binary.fromBytes(coldkey2.publicKey), - hotkey: Binary.fromBytes(hotkey.publicKey), - origin_netuid: netuid, - destination_netuid: netuid, - amount: transferAmount, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const stakeAfterOrigin = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - const stakeAfterDest = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey2.publicKey), - netuid, - ))?.stake - assert.ok(stakeAfterOrigin !== undefined && stakeAfterDest !== undefined) - assert.ok(stakeAfterOrigin < stakeBeforeOrigin!) - assert.ok(stakeAfterDest > stakeBeforeDest!) - }) - - it("Can caller swap_stake (fn 26)", async () => { - await addStakeViaContract(false) - await initSecondColdAndHotkey() - const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - const stakeBefore2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid + 1, - ))?.stake || BigInt(0) - assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) - const swapAmount = stakeBefore / BigInt(2) - const message = inkClient.message("caller_swap_stake") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - origin_netuid: netuid, - destination_netuid: netuid + 1, - amount: swapAmount, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - const stakeAfter2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid + 1, - ))?.stake - assert.ok(stakeAfter !== undefined && stakeAfter2 !== undefined) - assert.ok(stakeAfter < stakeBefore) - assert.ok(stakeAfter2 > stakeBefore2) - }) - - it("Can caller add_stake_limit (fn 27)", async () => { - const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeBefore !== undefined) - const message = inkClient.message("caller_add_stake_limit") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid, - amount: tao(200), - limit_price: tao(100), - allow_partial: false, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeAfter !== undefined && stakeAfter > stakeBefore!) - }) - - it("Can caller remove_stake_limit (fn 28)", async () => { - await addStakeViaContract(false) - const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) - const message = inkClient.message("caller_remove_stake_limit") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid, - amount: stakeBefore / BigInt(2), - limit_price: tao(1), - allow_partial: false, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeAfter !== undefined && stakeAfter < stakeBefore!) - }) - - it("Can caller swap_stake_limit (fn 29)", async () => { - await addStakeViaContract(false) - const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - const stakeBefore2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid + 1, - ))?.stake - assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) - assert.ok(stakeBefore2 !== undefined) - const message = inkClient.message("caller_swap_stake_limit") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - origin_netuid: netuid, - destination_netuid: netuid + 1, - amount: stakeBefore / BigInt(2), - limit_price: tao(1), - allow_partial: false, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - const stakeAfter2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid + 1, - ))?.stake - assert.ok(stakeAfter !== undefined && stakeAfter2 !== undefined) - assert.ok(stakeAfter < stakeBefore) - assert.ok(stakeAfter2 > stakeBefore2!) - }) - - it("Can caller remove_stake_full_limit (fn 30)", async () => { - await addStakeViaContract(false) - const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) - const message = inkClient.message("caller_remove_stake_full_limit") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid, - limit_price: undefined, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeAfter !== undefined && stakeAfter < stakeBefore!) - }) - - it("Can caller set_coldkey_auto_stake_hotkey (fn 31)", async () => { - await addStakeViaContract(false) - await initSecondColdAndHotkey() - const message = inkClient.message("caller_set_coldkey_auto_stake_hotkey") - const data = message.encode({ - netuid, - hotkey: Binary.fromBytes(hotkey2.publicKey), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const autoStakeHotkey = await api.query.SubtensorModule.AutoStakeDestination.getValue( - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ) - assert.ok(autoStakeHotkey === convertPublicKeyToSs58(hotkey2.publicKey)) - }) - - it("Can caller add_proxy and remove_proxy (fn 32-33)", async () => { - const addMessage = inkClient.message("caller_add_proxy") - const addData = addMessage.encode({ - delegate: Binary.fromBytes(hotkey2.publicKey), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, addData) - let proxies = await api.query.Proxy.Proxies.getValue(convertPublicKeyToSs58(coldkey.publicKey)) - assert.ok(proxies !== undefined && proxies[0].length > 0) - assert.ok(proxies[0][0].delegate === convertPublicKeyToSs58(hotkey2.publicKey)) - - const removeMessage = inkClient.message("caller_remove_proxy") - const removeData = removeMessage.encode({ - delegate: Binary.fromBytes(hotkey2.publicKey), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, removeData) - proxies = await api.query.Proxy.Proxies.getValue(convertPublicKeyToSs58(coldkey.publicKey)) - assert.ok(proxies !== undefined && proxies[0].length === 0) - }) - - it("Check add_stake_recycle is atomic operation", async () => { + it("Check add_stake_recycle is not atomic operation", async () => { const stakeBefore = await getContractStakeOnRoot() const balanceBefore = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)) + const contractBalanceBefore = (await api.query.System.Account.getValue(contractAddress)).data.free - // recycle alpha on root subnet is not allowed, the extrinsic should be failed. - const message = inkClient.message("add_stake_recycle") + const message = inkClient.message("add_stake_recycle_no_revert") const data = message.encode({ hotkey: Binary.fromBytes(hotkey.publicKey), netuid: 0, amount: tao(100), }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + + const signer = getSignerFromKeypair(coldkey) + await api.tx.Contracts.call({ + value: BigInt(0), + dest: MultiAddress.Id(contractAddress), + data: Binary.fromBytes(data.asBytes()), + gas_limit: { ref_time: BigInt(10_000_000_000), proof_size: BigInt(10_000_000) }, + storage_deposit_limit: BigInt(1_000_000_000), + }).signAndSubmit(signer) const stakeAfter = await getContractStakeOnRoot() const balanceAfter = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)) + const contractBalanceAfter = (await api.query.System.Account.getValue(contractAddress)).data.free - assert.ok(balanceBefore - balanceAfter < 10_000_000) - assert.equal(stakeAfter, stakeBefore) - }) - - it("Check add_stake_burn is atomic operation", async () => { - const stakeBefore = await getContractStakeOnRoot() - const balanceBefore = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)) - const alphaOutBefore = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) + console.log("stake: ", stakeBefore, "->", stakeAfter) + console.log("contract: ", contractBalanceBefore, "->", contractBalanceAfter) + console.log("coldkey: ", balanceBefore, "->", balanceAfter) - const message = inkClient.message("add_stake_burn") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid: 0, - amount: tao(100), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + assert.ok(balanceBefore - balanceAfter < 10_000_000) - const stakeAfter = await getContractStakeOnRoot() - const alphaOutAfter = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) - const balanceAfter = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)) + // BUG: root stake credited even though chain ext returned CannotBurnOrRecycleOnRootSubnet. + assert.ok( + stakeAfter > stakeBefore, + `BUG: not atomic root stake credited: before=${stakeBefore}, after=${stakeAfter}`, + ) - assert.ok(balanceBefore - balanceAfter < 10_000_000) - assert.equal(stakeAfter, stakeBefore) - assert.ok(alphaOutAfter > alphaOutBefore) + // BUG: TAO withdrawn from the CONTRACT account (env.caller()). + const contractDebit = contractBalanceBefore - contractBalanceAfter + assert.ok( + contractDebit >= tao(100) / BigInt(2), + `BUG: not atomic contract debited: debit=${contractDebit}`, + ) }) }); \ No newline at end of file From b33aa24c21ca07733b856f5ec50c2b84a48448ae Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 20 May 2026 20:18:58 +0800 Subject: [PATCH 5/5] revert to last commit --- contract-tests/bittensor/lib.rs | 29 - contract-tests/package.json | 2 +- contract-tests/test/wasm.contract.test.ts | 837 +++++++++++++++++++++- 3 files changed, 810 insertions(+), 58 deletions(-) diff --git a/contract-tests/bittensor/lib.rs b/contract-tests/bittensor/lib.rs index 85452f058c..bff61d3b08 100755 --- a/contract-tests/bittensor/lib.rs +++ b/contract-tests/bittensor/lib.rs @@ -801,34 +801,5 @@ mod bittensor { .caller_remove_proxy(delegate.into()) .map_err(|_e| ReadWriteErrorCode::WriteFailed) } - - #[ink(message)] - pub fn add_stake_recycle_no_revert( - &self, - hotkey: [u8; 32], - netuid: u16, - amount: u64, - ) -> u64 { - match self - .env() - .extension() - .add_stake_recycle(hotkey.into(), netuid, amount) - { - Ok(real_amount) => real_amount, - Err(_) => 0, - } - } - - #[ink(message)] - pub fn add_stake_burn_no_revert(&self, hotkey: [u8; 32], netuid: u16, amount: u64) -> u64 { - match self - .env() - .extension() - .add_stake_burn(hotkey.into(), netuid, amount) - { - Ok(real_amount) => real_amount, - Err(_) => 0, - } - } } } diff --git a/contract-tests/package.json b/contract-tests/package.json index 6b5eda8146..3acf069c1d 100644 --- a/contract-tests/package.json +++ b/contract-tests/package.json @@ -1,6 +1,6 @@ { "scripts": { - "test": "TS_NODE_PREFER_TS_EXTS=1 TS_NODE_TRANSPILE_ONLY=1 mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register --extension ts \"test/wasm.contract.test.ts\"" + "test": "TS_NODE_PREFER_TS_EXTS=1 TS_NODE_TRANSPILE_ONLY=1 mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register --extension ts \"test/**/*.ts\"" }, "keywords": [], "author": "", diff --git a/contract-tests/test/wasm.contract.test.ts b/contract-tests/test/wasm.contract.test.ts index a8e88b086a..6ae8d82c08 100644 --- a/contract-tests/test/wasm.contract.test.ts +++ b/contract-tests/test/wasm.contract.test.ts @@ -148,48 +148,829 @@ describe("Test wasm contract", () => { await waitForTransactionWithRetry(api, transfer, signer) }) - it("Check add_stake_recycle is not atomic operation", async () => { + + it("Can query stake info from contract", async () => { + + const queryMessage = inkClient.message("get_stake_info_for_hotkey_coldkey_netuid") + + const data = queryMessage.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + coldkey: Binary.fromBytes(coldkey.publicKey), + netuid: netuid, + }) + + const response = await api.apis.ContractsApi.call( + convertPublicKeyToSs58(hotkey.publicKey), + contractAddress, + BigInt(0), + undefined, + undefined, + Binary.fromBytes(data.asBytes()), + ) + + assert.ok(response.result.success) + const result = queryMessage.decode(response.result.value).value.value + + if (typeof result === "object" && "hotkey" in result && "coldkey" in result && "netuid" in result && "stake" in result && "locked" in result && "emission" in result && "tao_emission" in result && "drain" in result && "is_registered" in result) { + assert.equal(result.hotkey, convertPublicKeyToSs58(hotkey.publicKey)) + assert.equal(result.coldkey, convertPublicKeyToSs58(coldkey.publicKey)) + assert.equal(result.netuid, netuid) + assert.equal(result.is_registered, true) + } else { + throw new Error("result is not an object") + } + + }) + + it("Can add stake to contract", async () => { + await addStakeViaContract(true) + }) + + it("Can remove stake to contract", async () => { + await addStakeViaContract(true) + const stake = await getContractStake() + + let amount = stake / BigInt(2) + const message = inkClient.message("remove_stake") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid: netuid, + amount: amount, + }) + + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + + const stakeAfterAddStake = await getContractStake() + + assert.ok(stakeAfterAddStake < stake) + }) + + it("Can unstake all from contract", async () => { + await addStakeViaContract(true) + // Get stake before unstake_all + const stakeBefore = await getContractStake() + + assert.ok(stakeBefore > BigInt(0)) + + // Call unstake_all + const unstakeMessage = inkClient.message("unstake_all") + const unstakeData = unstakeMessage.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, unstakeData) + + // Verify stake is now zero + const stakeAfter = await getContractStake() + + assert.equal(stakeAfter, BigInt(0)) + }) + + it("Can unstake all alpha from contract", async () => { + await addStakeViaContract(true) + // Get stake before unstake_all_alpha + const stakeBefore = await getContractStake() + + assert.ok(stakeBefore > BigInt(0)) + + // Call unstake_all_alpha + const message = inkClient.message("unstake_all_alpha") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + + // Verify stake is now zero + const stakeAfter = await getContractStake() + + assert.equal(stakeAfter, BigInt(0)) + }) + + it("Can move stake between hotkeys", async () => { + await addStakeViaContract(true) + await initSecondColdAndHotkey() + // Get initial stakes + const originStakeBefore = await getContractStake() + + const destStakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey2.publicKey), + contractAddress, + netuid, + ))?.stake || BigInt(0) + + assert.ok(originStakeBefore > BigInt(0)) + + // Move stake + const moveAmount = originStakeBefore / BigInt(2) + const message = inkClient.message("move_stake") + const data = message.encode({ + origin_hotkey: Binary.fromBytes(hotkey.publicKey), + destination_hotkey: Binary.fromBytes(hotkey2.publicKey), + origin_netuid: netuid, + destination_netuid: netuid, + amount: moveAmount, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + + // Verify stakes changed + const originStakeAfter = await getContractStake() + + const destStakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey2.publicKey), + contractAddress, + netuid, + ))?.stake + + assert.ok(destStakeAfter !== undefined) + assert.ok(originStakeAfter < originStakeBefore) + assert.ok(destStakeAfter > destStakeBefore) + }) + + it("Can transfer stake between coldkeys", async () => { + await addStakeViaContract(true) + await initSecondColdAndHotkey() + // Get initial stake + const stakeBeforeOrigin = await getContractStake() + + const stakeBeforeDest = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey2.publicKey), + netuid, + ))?.stake + + assert.ok(stakeBeforeOrigin > BigInt(0)) + assert.ok(stakeBeforeDest !== undefined) + + // Transfer stake + const transferAmount = stakeBeforeOrigin / BigInt(2) + const message = inkClient.message("transfer_stake") + const data = message.encode({ + destination_coldkey: Binary.fromBytes(coldkey2.publicKey), + hotkey: Binary.fromBytes(hotkey.publicKey), + origin_netuid: netuid, + destination_netuid: netuid, + amount: transferAmount, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + + // Verify stake transferred + const stakeAfterOrigin = await getContractStake() + + const stakeAfterDest = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey2.publicKey), + netuid, + ))?.stake + + assert.ok(stakeAfterDest !== undefined) + assert.ok(stakeAfterOrigin < stakeBeforeOrigin) + assert.ok(stakeAfterDest > stakeBeforeDest!) + }) + + it("Can swap stake between networks", async () => { + await addStakeViaContract(true) + // Get initial stakes + const stakeBefore = await getContractStake() + + const stakeBefore2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + contractAddress, + netuid + 1, + ))?.stake || BigInt(0) + + assert.ok(stakeBefore > BigInt(0)) + + // Swap stake + const swapAmount = stakeBefore / BigInt(2) + const message = inkClient.message("swap_stake") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + origin_netuid: netuid, + destination_netuid: netuid + 1, + amount: swapAmount, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + + // Verify stakes swapped + const stakeAfter = await getContractStake() + + const stakeAfter2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + contractAddress, + netuid + 1, + ))?.stake + + assert.ok(stakeAfter2 !== undefined) + assert.ok(stakeAfter < stakeBefore) + assert.ok(stakeAfter2 > stakeBefore2) + }) + + it("Can add stake with limit", async () => { + const stakeBefore = await getContractStake() + + const message = inkClient.message("add_stake_limit") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid: netuid, + amount: tao(200), + limit_price: tao(100), + allow_partial: false, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + + // Verify stake was added + const stakeAfter = await getContractStake() + + assert.ok(stakeAfter > stakeBefore) + }) + + it("Can remove stake with limit", async () => { + await addStakeViaContract(true) + const stakeBefore = await getContractStake() + + assert.ok(stakeBefore > BigInt(0)) + + const message = inkClient.message("remove_stake_limit") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid: netuid, + amount: stakeBefore / BigInt(2), + limit_price: tao(1), + allow_partial: false, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + + const stakeAfter = await getContractStake() + + assert.ok(stakeAfter < stakeBefore) + }) + + it("Can swap stake with limit", async () => { + await addStakeViaContract(true) + + const stakeBefore = await getContractStake() + + const stakeBefore2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + contractAddress, + netuid + 1, + ))?.stake + + assert.ok(stakeBefore > BigInt(0)) + assert.ok(stakeBefore2 !== undefined) + + const message = inkClient.message("swap_stake_limit") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + origin_netuid: netuid, + destination_netuid: netuid + 1, + amount: stakeBefore / BigInt(2), + limit_price: tao(1), + allow_partial: false, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + + const stakeAfter = await getContractStake() + + const stakeAfter2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + contractAddress, + netuid + 1, + ))?.stake + + assert.ok(stakeAfter2 !== undefined) + assert.ok(stakeAfter < stakeBefore) + assert.ok(stakeAfter2 > stakeBefore2) + }) + + it("Can remove stake full limit", async () => { + await addStakeViaContract(true) + const stakeBefore = await getContractStake() + + assert.ok(stakeBefore > BigInt(0)) + + const message = inkClient.message("remove_stake_full_limit") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid: netuid, + limit_price: undefined, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + + const stakeAfter = await getContractStake() + + assert.ok(stakeAfter < stakeBefore) + }) + + it("Can set coldkey auto stake hotkey", async () => { + const message = inkClient.message("set_coldkey_auto_stake_hotkey") + const data = message.encode({ + netuid: netuid, + hotkey: Binary.fromBytes(hotkey.publicKey), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + + let autoStakeHotkey = await api.query.SubtensorModule.AutoStakeDestination.getValue( + contractAddress, + netuid, + ) + + assert.ok(autoStakeHotkey !== undefined) + assert.ok(autoStakeHotkey === convertPublicKeyToSs58(hotkey.publicKey)) + }) + + it("Can add and remove proxy", async () => { + const message = inkClient.message("add_proxy") + const data = message.encode({ + delegate: Binary.fromBytes(hotkey.publicKey), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + let proxies = await api.query.Proxy.Proxies.getValue( + contractAddress, + ) + assert.ok(proxies !== undefined) + assert.ok(proxies.length > 0 && proxies[0].length > 0) + assert.ok(proxies[0][0].delegate === convertPublicKeyToSs58(hotkey.publicKey)) + + + const removeMessage = inkClient.message("remove_proxy") + const removeData = removeMessage.encode({ + delegate: Binary.fromBytes(hotkey.publicKey), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, removeData) + + let proxiesAfterRemove = await api.query.Proxy.Proxies.getValue( + contractAddress, + ) + assert.ok(proxiesAfterRemove !== undefined) + assert.ok(proxiesAfterRemove[0].length === 0) + }) + + it("Can get alpha price", async () => { + const message = inkClient.message("get_alpha_price") + const data = message.encode({ + netuid: netuid, + }) + + const response = await api.apis.ContractsApi.call( + convertPublicKeyToSs58(hotkey.publicKey), + contractAddress, + BigInt(0), + undefined, + undefined, + Binary.fromBytes(data.asBytes()), + ) + + assert.ok(response.result.success) + const result = message.decode(response.result.value).value.value + + assert.ok(result !== undefined) + }) + + it("Can recycle alpha from contract stake", async () => { + await addStakeViaContract(true) + const stakeBefore = await getContractStake() + const alphaOutBefore = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) + + const message = inkClient.message("recycle_alpha") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + amount: stakeBefore / BigInt(2), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + + const stakeAfter = await getContractStake() + const alphaOutAfter = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) + + assert.ok(stakeAfter < stakeBefore) + assert.ok(alphaOutAfter < alphaOutBefore) + }) + + it("Can burn alpha from contract stake", async () => { + await addStakeViaContract(true) + const stakeBefore = await getContractStake() + const alphaBurnedBefore = await api.query.AlphaAssets.AlphaBurned.getValue(netuid) + + const message = inkClient.message("burn_alpha") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + amount: stakeBefore / BigInt(2), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + + const stakeAfter = await getContractStake() + const alphaBurnedAfter = await api.query.AlphaAssets.AlphaBurned.getValue(netuid) + + assert.ok(stakeAfter < stakeBefore) + assert.ok(alphaBurnedBefore < alphaBurnedAfter) + }) + + it("Can add stake and recycle resulting alpha", async () => { + const stakeBefore = await getContractStake() + + const message = inkClient.message("add_stake_recycle") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + amount: tao(100), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + + const stakeAfter = await getContractStake() + + assert.equal(stakeAfter, stakeBefore) + }) + + it("Can add stake and burn resulting alpha", async () => { + const stakeBefore = await getContractStake() + const alphaOutBefore = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) + + const message = inkClient.message("add_stake_burn") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + amount: tao(100), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + + const stakeAfter = await getContractStake() + const alphaOutAfter = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) + + assert.equal(stakeAfter, stakeBefore) + assert.ok(alphaOutAfter > alphaOutBefore) + }) + + it("Can caller add stake (fn 20)", async () => { + await addStakeViaContract(false) + }) + + it("Can caller remove stake (fn 21)", async () => { + await addStakeViaContract(false) + const stake = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stake !== undefined) + const amount = stake / BigInt(2) + const message = inkClient.message("caller_remove_stake") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + amount, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeAfter !== undefined && stakeAfter < stake!) + }) + + it("Can caller unstake_all (fn 22)", async () => { + await addStakeViaContract(false) + const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) + const message = inkClient.message("caller_unstake_all") + const data = message.encode({ hotkey: Binary.fromBytes(hotkey.publicKey) }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeAfter !== undefined) + assert.ok(stakeAfter < stakeBefore!) + }) + + it("Can caller unstake_all_alpha (fn 23)", async () => { + await addStakeViaContract(false) + const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) + const message = inkClient.message("caller_unstake_all_alpha") + const data = message.encode({ hotkey: Binary.fromBytes(hotkey.publicKey) }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeAfter !== undefined) + assert.ok(stakeAfter < stakeBefore!) + }) + + it("Can caller move_stake (fn 24)", async () => { + await addStakeViaContract(false) + await initSecondColdAndHotkey() + const originStakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + const destStakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey2.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake || BigInt(0) + assert.ok(originStakeBefore !== undefined && originStakeBefore > BigInt(0)) + const moveAmount = originStakeBefore / BigInt(2) + const message = inkClient.message("caller_move_stake") + const data = message.encode({ + origin_hotkey: Binary.fromBytes(hotkey.publicKey), + destination_hotkey: Binary.fromBytes(hotkey2.publicKey), + origin_netuid: netuid, + destination_netuid: netuid, + amount: moveAmount, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const originStakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + const destStakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey2.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(originStakeAfter !== undefined && destStakeAfter !== undefined) + assert.ok(originStakeAfter < originStakeBefore!) + assert.ok(destStakeAfter > destStakeBefore) + }) + + it("Can caller transfer_stake (fn 25)", async () => { + await addStakeViaContract(false) + await initSecondColdAndHotkey() + const stakeBeforeOrigin = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + const stakeBeforeDest = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey2.publicKey), + netuid, + ))?.stake + assert.ok(stakeBeforeOrigin !== undefined && stakeBeforeOrigin > BigInt(0)) + assert.ok(stakeBeforeDest !== undefined) + const transferAmount = stakeBeforeOrigin / BigInt(2) + const message = inkClient.message("caller_transfer_stake") + const data = message.encode({ + destination_coldkey: Binary.fromBytes(coldkey2.publicKey), + hotkey: Binary.fromBytes(hotkey.publicKey), + origin_netuid: netuid, + destination_netuid: netuid, + amount: transferAmount, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const stakeAfterOrigin = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + const stakeAfterDest = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey2.publicKey), + netuid, + ))?.stake + assert.ok(stakeAfterOrigin !== undefined && stakeAfterDest !== undefined) + assert.ok(stakeAfterOrigin < stakeBeforeOrigin!) + assert.ok(stakeAfterDest > stakeBeforeDest!) + }) + + it("Can caller swap_stake (fn 26)", async () => { + await addStakeViaContract(false) + await initSecondColdAndHotkey() + const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + const stakeBefore2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + 1, + ))?.stake || BigInt(0) + assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) + const swapAmount = stakeBefore / BigInt(2) + const message = inkClient.message("caller_swap_stake") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + origin_netuid: netuid, + destination_netuid: netuid + 1, + amount: swapAmount, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + const stakeAfter2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + 1, + ))?.stake + assert.ok(stakeAfter !== undefined && stakeAfter2 !== undefined) + assert.ok(stakeAfter < stakeBefore) + assert.ok(stakeAfter2 > stakeBefore2) + }) + + it("Can caller add_stake_limit (fn 27)", async () => { + const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeBefore !== undefined) + const message = inkClient.message("caller_add_stake_limit") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + amount: tao(200), + limit_price: tao(100), + allow_partial: false, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeAfter !== undefined && stakeAfter > stakeBefore!) + }) + + it("Can caller remove_stake_limit (fn 28)", async () => { + await addStakeViaContract(false) + const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) + const message = inkClient.message("caller_remove_stake_limit") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + amount: stakeBefore / BigInt(2), + limit_price: tao(1), + allow_partial: false, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeAfter !== undefined && stakeAfter < stakeBefore!) + }) + + it("Can caller swap_stake_limit (fn 29)", async () => { + await addStakeViaContract(false) + const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + const stakeBefore2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + 1, + ))?.stake + assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) + assert.ok(stakeBefore2 !== undefined) + const message = inkClient.message("caller_swap_stake_limit") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + origin_netuid: netuid, + destination_netuid: netuid + 1, + amount: stakeBefore / BigInt(2), + limit_price: tao(1), + allow_partial: false, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + const stakeAfter2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + 1, + ))?.stake + assert.ok(stakeAfter !== undefined && stakeAfter2 !== undefined) + assert.ok(stakeAfter < stakeBefore) + assert.ok(stakeAfter2 > stakeBefore2!) + }) + + it("Can caller remove_stake_full_limit (fn 30)", async () => { + await addStakeViaContract(false) + const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) + const message = inkClient.message("caller_remove_stake_full_limit") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + limit_price: undefined, + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ))?.stake + assert.ok(stakeAfter !== undefined && stakeAfter < stakeBefore!) + }) + + it("Can caller set_coldkey_auto_stake_hotkey (fn 31)", async () => { + await addStakeViaContract(false) + await initSecondColdAndHotkey() + const message = inkClient.message("caller_set_coldkey_auto_stake_hotkey") + const data = message.encode({ + netuid, + hotkey: Binary.fromBytes(hotkey2.publicKey), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + const autoStakeHotkey = await api.query.SubtensorModule.AutoStakeDestination.getValue( + convertPublicKeyToSs58(coldkey.publicKey), + netuid, + ) + assert.ok(autoStakeHotkey === convertPublicKeyToSs58(hotkey2.publicKey)) + }) + + it("Can caller add_proxy and remove_proxy (fn 32-33)", async () => { + const addMessage = inkClient.message("caller_add_proxy") + const addData = addMessage.encode({ + delegate: Binary.fromBytes(hotkey2.publicKey), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, addData) + let proxies = await api.query.Proxy.Proxies.getValue(convertPublicKeyToSs58(coldkey.publicKey)) + assert.ok(proxies !== undefined && proxies[0].length > 0) + assert.ok(proxies[0][0].delegate === convertPublicKeyToSs58(hotkey2.publicKey)) + + const removeMessage = inkClient.message("caller_remove_proxy") + const removeData = removeMessage.encode({ + delegate: Binary.fromBytes(hotkey2.publicKey), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, removeData) + proxies = await api.query.Proxy.Proxies.getValue(convertPublicKeyToSs58(coldkey.publicKey)) + assert.ok(proxies !== undefined && proxies[0].length === 0) + }) + + it("Check add_stake_recycle is atomic operation", async () => { const stakeBefore = await getContractStakeOnRoot() const balanceBefore = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)) - const contractBalanceBefore = (await api.query.System.Account.getValue(contractAddress)).data.free - const message = inkClient.message("add_stake_recycle_no_revert") + // recycle alpha on root subnet is not allowed, the extrinsic should be failed. + const message = inkClient.message("add_stake_recycle") const data = message.encode({ hotkey: Binary.fromBytes(hotkey.publicKey), netuid: 0, amount: tao(100), }) - - const signer = getSignerFromKeypair(coldkey) - await api.tx.Contracts.call({ - value: BigInt(0), - dest: MultiAddress.Id(contractAddress), - data: Binary.fromBytes(data.asBytes()), - gas_limit: { ref_time: BigInt(10_000_000_000), proof_size: BigInt(10_000_000) }, - storage_deposit_limit: BigInt(1_000_000_000), - }).signAndSubmit(signer) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) const stakeAfter = await getContractStakeOnRoot() const balanceAfter = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)) - const contractBalanceAfter = (await api.query.System.Account.getValue(contractAddress)).data.free - - console.log("stake: ", stakeBefore, "->", stakeAfter) - console.log("contract: ", contractBalanceBefore, "->", contractBalanceAfter) - console.log("coldkey: ", balanceBefore, "->", balanceAfter) assert.ok(balanceBefore - balanceAfter < 10_000_000) + assert.equal(stakeAfter, stakeBefore) + }) - // BUG: root stake credited even though chain ext returned CannotBurnOrRecycleOnRootSubnet. - assert.ok( - stakeAfter > stakeBefore, - `BUG: not atomic root stake credited: before=${stakeBefore}, after=${stakeAfter}`, - ) + it("Check add_stake_burn is atomic operation", async () => { + const stakeBefore = await getContractStakeOnRoot() + const balanceBefore = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)) + const alphaOutBefore = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) - // BUG: TAO withdrawn from the CONTRACT account (env.caller()). - const contractDebit = contractBalanceBefore - contractBalanceAfter - assert.ok( - contractDebit >= tao(100) / BigInt(2), - `BUG: not atomic contract debited: debit=${contractDebit}`, - ) + const message = inkClient.message("add_stake_burn") + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid: 0, + amount: tao(100), + }) + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) + + const stakeAfter = await getContractStakeOnRoot() + const alphaOutAfter = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) + const balanceAfter = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)) + + assert.ok(balanceBefore - balanceAfter < 10_000_000) + assert.equal(stakeAfter, stakeBefore) + assert.ok(alphaOutAfter > alphaOutBefore) }) }); \ No newline at end of file