Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 33 additions & 10 deletions evm-tests/src/contracts/staking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const IStakingABI = [
],
name: "addProxy",
outputs: [],
stateMutability: "nonpayable",
stateMutability: "payable",
type: "function",
},
{
Expand Down Expand Up @@ -43,7 +43,7 @@ export const IStakingABI = [
],
name: "removeProxy",
outputs: [],
stateMutability: "nonpayable",
stateMutability: "payable",
type: "function",
},
{
Expand Down Expand Up @@ -95,7 +95,7 @@ export const IStakingABI = [
],
name: "removeStake",
outputs: [],
stateMutability: "nonpayable",
stateMutability: "payable",
type: "function",
},
];
Expand All @@ -111,7 +111,7 @@ export const IStakingV2ABI = [
],
"name": "addProxy",
"outputs": [],
"stateMutability": "nonpayable",
"stateMutability": "payable",
"type": "function"
},
{
Expand Down Expand Up @@ -275,7 +275,7 @@ export const IStakingV2ABI = [
],
"name": "removeProxy",
"outputs": [],
"stateMutability": "nonpayable",
"stateMutability": "payable",
"type": "function"
},
{
Expand All @@ -298,7 +298,7 @@ export const IStakingV2ABI = [
],
"name": "removeStake",
"outputs": [],
"stateMutability": "nonpayable",
"stateMutability": "payable",
"type": "function"
},
{
Expand Down Expand Up @@ -331,7 +331,7 @@ export const IStakingV2ABI = [
],
"name": "addStakeLimit",
"outputs": [],
"stateMutability": "nonpayable",
"stateMutability": "payable",
"type": "function"
},
{
Expand Down Expand Up @@ -364,7 +364,7 @@ export const IStakingV2ABI = [
],
"name": "removeStakeLimit",
"outputs": [],
"stateMutability": "nonpayable",
"stateMutability": "payable",
"type": "function"
},
{
Expand All @@ -382,7 +382,7 @@ export const IStakingV2ABI = [
],
"name": "removeStakeFull",
"outputs": [],
"stateMutability": "nonpayable",
"stateMutability": "payable",
"type": "function"
},
{
Expand All @@ -405,7 +405,30 @@ export const IStakingV2ABI = [
],
"name": "removeStakeFullLimit",
"outputs": [],
"stateMutability": "nonpayable",
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "hotkey",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "netuid",
"type": "uint256"
}
],
"name": "burnAlpha",
"outputs": [],
"stateMutability": "payable",
"type": "function"
}
];
136 changes: 136 additions & 0 deletions evm-tests/test/staking.precompile.burn-alpha.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import * as assert from "assert";
import { getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate"
import { devnet } from "@polkadot-api/descriptors"
import { TypedApi } from "polkadot-api";
import { convertPublicKeyToSs58, convertH160ToSS58 } from "../src/address-utils"
import { tao } from "../src/balance-math"
import { ethers } from "ethers"
import { generateRandomEthersWallet } from "../src/utils"
import { convertH160ToPublicKey } from "../src/address-utils"
import {
forceSetBalanceToEthAddress, forceSetBalanceToSs58Address, addNewSubnetwork, burnedRegister,
startCall,
} from "../src/subtensor"
import { ISTAKING_V2_ADDRESS, IStakingV2ABI } from "../src/contracts/staking"

describe("Test staking precompile burn alpha", () => {
// init eth part
const wallet1 = generateRandomEthersWallet();
// init substrate part
const hotkey = getRandomSubstrateKeypair();
const coldkey = getRandomSubstrateKeypair();

let api: TypedApi<typeof devnet>

before(async () => {
// init variables got from await and async
api = await getDevnetApi()

await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey))
await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey))
await forceSetBalanceToEthAddress(api, wallet1.address)

let netuid = await addNewSubnetwork(api, hotkey, coldkey)
await startCall(api, netuid, coldkey)

console.log("test the case on subnet ", netuid)

await burnedRegister(api, netuid, convertH160ToSS58(wallet1.address), coldkey)
})

it("Can burn alpha after adding stake", async () => {
let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1

// First add some stake
let stakeBalance = tao(50)
const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet1);
const addStakeTx = await contract.addStake(hotkey.publicKey, stakeBalance.toString(), netuid)
await addStakeTx.wait()

// Get stake before burning
const stakeBefore = BigInt(await contract.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet1.address), netuid))

console.log("Stake before burn:", stakeBefore)
assert.ok(stakeBefore > BigInt(0), "Should have stake before burning")

// Burn some alpha (burn 20 TAO worth)
let burnAmount = tao(20)
const burnTx = await contract.burnAlpha(hotkey.publicKey, burnAmount.toString(), netuid)
await burnTx.wait()

// Get stake after burning
const stakeAfter = BigInt(await contract.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet1.address), netuid))

console.log("Stake after burn:", stakeAfter)

// Verify that stake decreased by burn amount
assert.ok(stakeAfter < stakeBefore, "Stake should decrease after burning")
// assert.strictEqual(stakeBefore - stakeAfter, burnAmount, "Stake should decrease by exactly burn amount")
})

it("Cannot burn more alpha than staked", async () => {
let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1

// Get current stake
const currentStake = await api.query.SubtensorModule.Alpha.getValue(
convertPublicKeyToSs58(hotkey.publicKey),
convertH160ToSS58(wallet1.address),
netuid
)

// Try to burn more than staked
let burnAmount = currentStake + tao(10000)
const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet1);

try {
const burnTx = await contract.burnAlpha(hotkey.publicKey, burnAmount.toString(), netuid)
await burnTx.wait()
assert.fail("Transaction should have failed - cannot burn more than staked");
} catch (error) {
// Transaction failed as expected
console.log("Correctly failed to burn more than staked amount")
assert.ok(true, "Burning more than staked should fail");
}
})

it("Cannot burn alpha from non-existent subnet", async () => {
// wrong netuid
let netuid = 12345;
let burnAmount = tao(10)
const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet1);

try {
const burnTx = await contract.burnAlpha(hotkey.publicKey, burnAmount.toString(), netuid)
await burnTx.wait()
assert.fail("Transaction should have failed - subnet doesn't exist");
} catch (error) {
// Transaction failed as expected
console.log("Correctly failed to burn from non-existent subnet")
assert.ok(true, "Burning from non-existent subnet should fail");
}
})

it("Cannot burn zero alpha", async () => {
let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1

// First add some stake for this test
let stakeBalance = tao(10)
const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet1);
const addStakeTx = await contract.addStake(hotkey.publicKey, stakeBalance.toString(), netuid)
await addStakeTx.wait()

// Try to burn zero amount
let burnAmount = BigInt(0)

try {
const burnTx = await contract.burnAlpha(hotkey.publicKey, burnAmount.toString(), netuid)
await burnTx.wait()
assert.fail("Transaction should have failed - cannot burn zero amount");
} catch (error) {
// Transaction failed as expected
console.log("Correctly failed to burn zero amount")
assert.ok(true, "Burning zero amount should fail");
}
})
})

39 changes: 31 additions & 8 deletions precompiles/src/solidity/stakingV2.abi
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
],
"name": "addProxy",
"outputs": [],
"stateMutability": "nonpayable",
"stateMutability": "payable",
"type": "function"
},
{
Expand Down Expand Up @@ -226,7 +226,7 @@
],
"name": "moveStake",
"outputs": [],
"stateMutability": "nonpayable",
"stateMutability": "payable",
"type": "function"
},
{
Expand All @@ -239,7 +239,7 @@
],
"name": "removeProxy",
"outputs": [],
"stateMutability": "nonpayable",
"stateMutability": "payable",
"type": "function"
},
{
Expand All @@ -262,7 +262,7 @@
],
"name": "removeStake",
"outputs": [],
"stateMutability": "nonpayable",
"stateMutability": "payable",
"type": "function"
},
{
Expand All @@ -280,7 +280,7 @@
],
"name": "removeStakeFull",
"outputs": [],
"stateMutability": "nonpayable",
"stateMutability": "payable",
"type": "function"
},
{
Expand All @@ -303,7 +303,7 @@
],
"name": "removeStakeFullLimit",
"outputs": [],
"stateMutability": "nonpayable",
"stateMutability": "payable",
"type": "function"
},
{
Expand Down Expand Up @@ -336,7 +336,7 @@
],
"name": "removeStakeLimit",
"outputs": [],
"stateMutability": "nonpayable",
"stateMutability": "payable",
"type": "function"
},
{
Expand Down Expand Up @@ -369,7 +369,30 @@
],
"name": "transferStake",
"outputs": [],
"stateMutability": "nonpayable",
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "hotkey",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "netuid",
"type": "uint256"
}
],
"name": "burnAlpha",
"outputs": [],
"stateMutability": "payable",
"type": "function"
}
]
Loading
Loading