Skip to content

Commit 99a542b

Browse files
authored
Merge pull request #2137 from opentensor/burn-alpha-precompile
burn alpha precompile
2 parents 4b83813 + d03be2e commit 99a542b

File tree

5 files changed

+251
-26
lines changed

5 files changed

+251
-26
lines changed

evm-tests/src/contracts/staking.ts

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const IStakingABI = [
1212
],
1313
name: "addProxy",
1414
outputs: [],
15-
stateMutability: "nonpayable",
15+
stateMutability: "payable",
1616
type: "function",
1717
},
1818
{
@@ -43,7 +43,7 @@ export const IStakingABI = [
4343
],
4444
name: "removeProxy",
4545
outputs: [],
46-
stateMutability: "nonpayable",
46+
stateMutability: "payable",
4747
type: "function",
4848
},
4949
{
@@ -95,7 +95,7 @@ export const IStakingABI = [
9595
],
9696
name: "removeStake",
9797
outputs: [],
98-
stateMutability: "nonpayable",
98+
stateMutability: "payable",
9999
type: "function",
100100
},
101101
];
@@ -111,7 +111,7 @@ export const IStakingV2ABI = [
111111
],
112112
"name": "addProxy",
113113
"outputs": [],
114-
"stateMutability": "nonpayable",
114+
"stateMutability": "payable",
115115
"type": "function"
116116
},
117117
{
@@ -275,7 +275,7 @@ export const IStakingV2ABI = [
275275
],
276276
"name": "removeProxy",
277277
"outputs": [],
278-
"stateMutability": "nonpayable",
278+
"stateMutability": "payable",
279279
"type": "function"
280280
},
281281
{
@@ -298,7 +298,7 @@ export const IStakingV2ABI = [
298298
],
299299
"name": "removeStake",
300300
"outputs": [],
301-
"stateMutability": "nonpayable",
301+
"stateMutability": "payable",
302302
"type": "function"
303303
},
304304
{
@@ -331,7 +331,7 @@ export const IStakingV2ABI = [
331331
],
332332
"name": "addStakeLimit",
333333
"outputs": [],
334-
"stateMutability": "nonpayable",
334+
"stateMutability": "payable",
335335
"type": "function"
336336
},
337337
{
@@ -364,7 +364,7 @@ export const IStakingV2ABI = [
364364
],
365365
"name": "removeStakeLimit",
366366
"outputs": [],
367-
"stateMutability": "nonpayable",
367+
"stateMutability": "payable",
368368
"type": "function"
369369
},
370370
{
@@ -382,7 +382,7 @@ export const IStakingV2ABI = [
382382
],
383383
"name": "removeStakeFull",
384384
"outputs": [],
385-
"stateMutability": "nonpayable",
385+
"stateMutability": "payable",
386386
"type": "function"
387387
},
388388
{
@@ -405,7 +405,30 @@ export const IStakingV2ABI = [
405405
],
406406
"name": "removeStakeFullLimit",
407407
"outputs": [],
408-
"stateMutability": "nonpayable",
408+
"stateMutability": "payable",
409+
"type": "function"
410+
},
411+
{
412+
"inputs": [
413+
{
414+
"internalType": "bytes32",
415+
"name": "hotkey",
416+
"type": "bytes32"
417+
},
418+
{
419+
"internalType": "uint256",
420+
"name": "amount",
421+
"type": "uint256"
422+
},
423+
{
424+
"internalType": "uint256",
425+
"name": "netuid",
426+
"type": "uint256"
427+
}
428+
],
429+
"name": "burnAlpha",
430+
"outputs": [],
431+
"stateMutability": "payable",
409432
"type": "function"
410433
}
411434
];
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import * as assert from "assert";
2+
import { getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate"
3+
import { devnet } from "@polkadot-api/descriptors"
4+
import { TypedApi } from "polkadot-api";
5+
import { convertPublicKeyToSs58, convertH160ToSS58 } from "../src/address-utils"
6+
import { tao } from "../src/balance-math"
7+
import { ethers } from "ethers"
8+
import { generateRandomEthersWallet } from "../src/utils"
9+
import { convertH160ToPublicKey } from "../src/address-utils"
10+
import {
11+
forceSetBalanceToEthAddress, forceSetBalanceToSs58Address, addNewSubnetwork, burnedRegister,
12+
startCall,
13+
} from "../src/subtensor"
14+
import { ISTAKING_V2_ADDRESS, IStakingV2ABI } from "../src/contracts/staking"
15+
16+
describe("Test staking precompile burn alpha", () => {
17+
// init eth part
18+
const wallet1 = generateRandomEthersWallet();
19+
// init substrate part
20+
const hotkey = getRandomSubstrateKeypair();
21+
const coldkey = getRandomSubstrateKeypair();
22+
23+
let api: TypedApi<typeof devnet>
24+
25+
before(async () => {
26+
// init variables got from await and async
27+
api = await getDevnetApi()
28+
29+
await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey))
30+
await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey))
31+
await forceSetBalanceToEthAddress(api, wallet1.address)
32+
33+
let netuid = await addNewSubnetwork(api, hotkey, coldkey)
34+
await startCall(api, netuid, coldkey)
35+
36+
console.log("test the case on subnet ", netuid)
37+
38+
await burnedRegister(api, netuid, convertH160ToSS58(wallet1.address), coldkey)
39+
})
40+
41+
it("Can burn alpha after adding stake", async () => {
42+
let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1
43+
44+
// First add some stake
45+
let stakeBalance = tao(50)
46+
const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet1);
47+
const addStakeTx = await contract.addStake(hotkey.publicKey, stakeBalance.toString(), netuid)
48+
await addStakeTx.wait()
49+
50+
// Get stake before burning
51+
const stakeBefore = BigInt(await contract.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet1.address), netuid))
52+
53+
console.log("Stake before burn:", stakeBefore)
54+
assert.ok(stakeBefore > BigInt(0), "Should have stake before burning")
55+
56+
// Burn some alpha (burn 20 TAO worth)
57+
let burnAmount = tao(20)
58+
const burnTx = await contract.burnAlpha(hotkey.publicKey, burnAmount.toString(), netuid)
59+
await burnTx.wait()
60+
61+
// Get stake after burning
62+
const stakeAfter = BigInt(await contract.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet1.address), netuid))
63+
64+
console.log("Stake after burn:", stakeAfter)
65+
66+
// Verify that stake decreased by burn amount
67+
assert.ok(stakeAfter < stakeBefore, "Stake should decrease after burning")
68+
// assert.strictEqual(stakeBefore - stakeAfter, burnAmount, "Stake should decrease by exactly burn amount")
69+
})
70+
71+
it("Cannot burn more alpha than staked", async () => {
72+
let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1
73+
74+
// Get current stake
75+
const currentStake = await api.query.SubtensorModule.Alpha.getValue(
76+
convertPublicKeyToSs58(hotkey.publicKey),
77+
convertH160ToSS58(wallet1.address),
78+
netuid
79+
)
80+
81+
// Try to burn more than staked
82+
let burnAmount = currentStake + tao(10000)
83+
const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet1);
84+
85+
try {
86+
const burnTx = await contract.burnAlpha(hotkey.publicKey, burnAmount.toString(), netuid)
87+
await burnTx.wait()
88+
assert.fail("Transaction should have failed - cannot burn more than staked");
89+
} catch (error) {
90+
// Transaction failed as expected
91+
console.log("Correctly failed to burn more than staked amount")
92+
assert.ok(true, "Burning more than staked should fail");
93+
}
94+
})
95+
96+
it("Cannot burn alpha from non-existent subnet", async () => {
97+
// wrong netuid
98+
let netuid = 12345;
99+
let burnAmount = tao(10)
100+
const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet1);
101+
102+
try {
103+
const burnTx = await contract.burnAlpha(hotkey.publicKey, burnAmount.toString(), netuid)
104+
await burnTx.wait()
105+
assert.fail("Transaction should have failed - subnet doesn't exist");
106+
} catch (error) {
107+
// Transaction failed as expected
108+
console.log("Correctly failed to burn from non-existent subnet")
109+
assert.ok(true, "Burning from non-existent subnet should fail");
110+
}
111+
})
112+
113+
it("Cannot burn zero alpha", async () => {
114+
let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1
115+
116+
// First add some stake for this test
117+
let stakeBalance = tao(10)
118+
const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet1);
119+
const addStakeTx = await contract.addStake(hotkey.publicKey, stakeBalance.toString(), netuid)
120+
await addStakeTx.wait()
121+
122+
// Try to burn zero amount
123+
let burnAmount = BigInt(0)
124+
125+
try {
126+
const burnTx = await contract.burnAlpha(hotkey.publicKey, burnAmount.toString(), netuid)
127+
await burnTx.wait()
128+
assert.fail("Transaction should have failed - cannot burn zero amount");
129+
} catch (error) {
130+
// Transaction failed as expected
131+
console.log("Correctly failed to burn zero amount")
132+
assert.ok(true, "Burning zero amount should fail");
133+
}
134+
})
135+
})
136+

precompiles/src/solidity/stakingV2.abi

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
],
1010
"name": "addProxy",
1111
"outputs": [],
12-
"stateMutability": "nonpayable",
12+
"stateMutability": "payable",
1313
"type": "function"
1414
},
1515
{
@@ -226,7 +226,7 @@
226226
],
227227
"name": "moveStake",
228228
"outputs": [],
229-
"stateMutability": "nonpayable",
229+
"stateMutability": "payable",
230230
"type": "function"
231231
},
232232
{
@@ -239,7 +239,7 @@
239239
],
240240
"name": "removeProxy",
241241
"outputs": [],
242-
"stateMutability": "nonpayable",
242+
"stateMutability": "payable",
243243
"type": "function"
244244
},
245245
{
@@ -262,7 +262,7 @@
262262
],
263263
"name": "removeStake",
264264
"outputs": [],
265-
"stateMutability": "nonpayable",
265+
"stateMutability": "payable",
266266
"type": "function"
267267
},
268268
{
@@ -280,7 +280,7 @@
280280
],
281281
"name": "removeStakeFull",
282282
"outputs": [],
283-
"stateMutability": "nonpayable",
283+
"stateMutability": "payable",
284284
"type": "function"
285285
},
286286
{
@@ -303,7 +303,7 @@
303303
],
304304
"name": "removeStakeFullLimit",
305305
"outputs": [],
306-
"stateMutability": "nonpayable",
306+
"stateMutability": "payable",
307307
"type": "function"
308308
},
309309
{
@@ -336,7 +336,7 @@
336336
],
337337
"name": "removeStakeLimit",
338338
"outputs": [],
339-
"stateMutability": "nonpayable",
339+
"stateMutability": "payable",
340340
"type": "function"
341341
},
342342
{
@@ -369,7 +369,30 @@
369369
],
370370
"name": "transferStake",
371371
"outputs": [],
372-
"stateMutability": "nonpayable",
372+
"stateMutability": "payable",
373+
"type": "function"
374+
},
375+
{
376+
"inputs": [
377+
{
378+
"internalType": "bytes32",
379+
"name": "hotkey",
380+
"type": "bytes32"
381+
},
382+
{
383+
"internalType": "uint256",
384+
"name": "amount",
385+
"type": "uint256"
386+
},
387+
{
388+
"internalType": "uint256",
389+
"name": "netuid",
390+
"type": "uint256"
391+
}
392+
],
393+
"name": "burnAlpha",
394+
"outputs": [],
395+
"stateMutability": "payable",
373396
"type": "function"
374397
}
375398
]

0 commit comments

Comments
 (0)