Skip to content

Stake wrap contract #1763

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
Jul 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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
65 changes: 65 additions & 0 deletions evm-tests/src/contracts/stakeWrap.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: GPL-3.0
// need use the compiler version 0.8.20 for this contract, otherwise there is an issue
// opcode(94) swap5 not supported.
pragma solidity >=0.8.0 <0.8.2;

address constant ISTAKING_ADDRESS = 0x0000000000000000000000000000000000000805;

interface Staking {
function addStakeLimit(
bytes32 hotkey,
uint256 amount,
uint256 limit_price,
bool allow_partial,
uint256 netuid
) external;

function addStake(bytes32 hotkey, uint256 amount, uint256 netuid) external;
}

contract StakeWrap {
constructor() {}
receive() external payable {}

function stake(bytes32 hotkey, uint256 netuid, uint256 amount) external {
// can't call precompile like this way, the call never go to runtime precompile
//Staking(ISTAKING_ADDRESS).addStake(hotkey, amount, netuid);

bytes memory data = abi.encodeWithSelector(
Staking.addStake.selector,
hotkey,
amount,
netuid
);
(bool success, ) = ISTAKING_ADDRESS.call{gas: gasleft()}(data);
require(success, "addStake call failed");
}

function stakeLimit(
bytes32 hotkey,
uint256 netuid,
uint256 limitPrice,
uint256 amount,
bool allowPartial
) external {
// can't call precompile like this way, the call never go to runtime precompile
// Staking(ISTAKING_ADDRESS).addStakeLimit(
// hotkey,
// amount,
// limitPrice,
// allowPartial,
// netuid
// );

bytes memory data = abi.encodeWithSelector(
Staking.addStakeLimit.selector,
hotkey,
amount,
limitPrice,
allowPartial,
netuid
);
(bool success, ) = ISTAKING_ADDRESS.call{gas: gasleft()}(data);
require(success, "addStakeLimit call failed");
}
}
66 changes: 66 additions & 0 deletions evm-tests/src/contracts/stakeWrap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
export const abi = [
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "hotkey",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "netuid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "stake",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "hotkey",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "netuid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "limitPrice",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "bool",
"name": "allowPartial",
"type": "bool"
}
],
"name": "stakeLimit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
];

// compiled with 0.8.20
export const bytecode = "608060405234801561000f575f80fd5b5061069e8061001d5f395ff3fe60806040526004361061002c575f3560e01c80632daedd521461003757806390b9d5341461005f57610033565b3661003357005b5f80fd5b348015610042575f80fd5b5061005d60048036038101906100589190610357565b610087565b005b34801561006a575f80fd5b50610085600480360381019061008091906103dc565b6101b7565b005b5f631fc9b14160e01b8483856040516024016100a593929190610471565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f61080573ffffffffffffffffffffffffffffffffffffffff165a8360405161012d9190610512565b5f604051808303815f8787f1925050503d805f8114610167576040519150601f19603f3d011682016040523d82523d5f602084013e61016c565b606091505b50509050806101b0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101a790610582565b60405180910390fd5b5050505050565b5f635beb6b7460e01b86848685896040516024016101d99594939291906105af565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f61080573ffffffffffffffffffffffffffffffffffffffff165a836040516102619190610512565b5f604051808303815f8787f1925050503d805f811461029b576040519150601f19603f3d011682016040523d82523d5f602084013e6102a0565b606091505b50509050806102e4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102db9061064a565b60405180910390fd5b50505050505050565b5f80fd5b5f819050919050565b610303816102f1565b811461030d575f80fd5b50565b5f8135905061031e816102fa565b92915050565b5f819050919050565b61033681610324565b8114610340575f80fd5b50565b5f813590506103518161032d565b92915050565b5f805f6060848603121561036e5761036d6102ed565b5b5f61037b86828701610310565b935050602061038c86828701610343565b925050604061039d86828701610343565b9150509250925092565b5f8115159050919050565b6103bb816103a7565b81146103c5575f80fd5b50565b5f813590506103d6816103b2565b92915050565b5f805f805f60a086880312156103f5576103f46102ed565b5b5f61040288828901610310565b955050602061041388828901610343565b945050604061042488828901610343565b935050606061043588828901610343565b9250506080610446888289016103c8565b9150509295509295909350565b61045c816102f1565b82525050565b61046b81610324565b82525050565b5f6060820190506104845f830186610453565b6104916020830185610462565b61049e6040830184610462565b949350505050565b5f81519050919050565b5f81905092915050565b5f5b838110156104d75780820151818401526020810190506104bc565b5f8484015250505050565b5f6104ec826104a6565b6104f681856104b0565b93506105068185602086016104ba565b80840191505092915050565b5f61051d82846104e2565b915081905092915050565b5f82825260208201905092915050565b7f6164645374616b652063616c6c206661696c65640000000000000000000000005f82015250565b5f61056c601483610528565b915061057782610538565b602082019050919050565b5f6020820190508181035f83015261059981610560565b9050919050565b6105a9816103a7565b82525050565b5f60a0820190506105c25f830188610453565b6105cf6020830187610462565b6105dc6040830186610462565b6105e960608301856105a0565b6105f66080830184610462565b9695505050505050565b7f6164645374616b654c696d69742063616c6c206661696c6564000000000000005f82015250565b5f610634601983610528565b915061063f82610600565b602082019050919050565b5f6020820190508181035f83015261066181610628565b905091905056fea264697066735822122083351bec20bd75de90a1b6e405922bedadf9ff260c02f34ef9dbb5ee1bda11cd64736f6c63430008140033"
2 changes: 1 addition & 1 deletion evm-tests/test/eth.substrate-transfer.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as assert from "assert";

import { getDevnetApi, waitForTransactionCompletion, getRandomSubstrateSigner, waitForTransactionWithRetry} from "../src/substrate"
import { getDevnetApi, waitForTransactionCompletion, getRandomSubstrateSigner, waitForTransactionWithRetry } from "../src/substrate"
import { getPublicClient } from "../src/utils";
import { ETH_LOCAL_URL, IBALANCETRANSFER_ADDRESS, IBalanceTransferABI } from "../src/config";
import { devnet, MultiAddress } from "@polkadot-api/descriptors"
Expand Down
122 changes: 122 additions & 0 deletions evm-tests/test/staking.precompile.wrap.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import * as assert from "assert";
import { getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate";
import { devnet } from "@polkadot-api/descriptors";
import { TypedApi } from "polkadot-api";
import {
convertH160ToSS58,
convertPublicKeyToSs58,
ethAddressToH160,
} from "../src/address-utils";
import { tao, raoToEth } from "../src/balance-math";
import {
addNewSubnetwork,
addStake,
disableWhiteListCheck,
forceSetBalanceToEthAddress,
forceSetBalanceToSs58Address,
startCall,
} from "../src/subtensor";
import { ethers } from "ethers";
import { generateRandomEthersWallet } from "../src/utils";
import { log } from "console";

import { abi, bytecode } from "../src/contracts/stakeWrap";

describe("Test staking precompile add from deployed contract", () => {
const hotkey = getRandomSubstrateKeypair();
const coldkey = getRandomSubstrateKeypair();
const wallet1 = generateRandomEthersWallet();

let api: TypedApi<typeof devnet>;

before(async () => {
api = await getDevnetApi();
await forceSetBalanceToSs58Address(
api,
convertPublicKeyToSs58(hotkey.publicKey),
);
await forceSetBalanceToSs58Address(
api,
convertPublicKeyToSs58(coldkey.publicKey),
);
await forceSetBalanceToEthAddress(api, wallet1.address);
await addNewSubnetwork(api, hotkey, coldkey);
let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1;
await startCall(api, netuid, coldkey);
await disableWhiteListCheck(api, true)
console.log("will test in subnet: ", netuid);
});

it("Staker add stake", async () => {
let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1;

const contractFactory = new ethers.ContractFactory(abi, bytecode, wallet1)
const contract = await contractFactory.deploy()
await contract.waitForDeployment()


// stake will remove the balance from contract, need transfer token to deployed contract
const ethTransfer = {
to: contract.target.toString(),
value: raoToEth(tao(10000)).toString()
}

const txResponse = await wallet1.sendTransaction(ethTransfer)
await txResponse.wait();

const balance = await api.query.System.Account.getValue(convertH160ToSS58(contract.target.toString()))
console.log(" == balance is ", balance.data.free)

const deployedContract = new ethers.Contract(
contract.target.toString(),
abi,
wallet1,
);

const tx = await deployedContract.stake(
hotkey.publicKey,
netuid,
tao(2000),
);
await tx.wait();

});

it("Staker add stake limit", async () => {
let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1;
let ss58Address = convertH160ToSS58(wallet1.address);

const contractFactory = new ethers.ContractFactory(abi, bytecode, wallet1)
const contract = await contractFactory.deploy()
await contract.waitForDeployment()


// stake will remove the balance from contract, need transfer token to deployed contract
const ethTransfer = {
to: contract.target.toString(),
value: raoToEth(tao(10000)).toString()
}

const txResponse = await wallet1.sendTransaction(ethTransfer)
await txResponse.wait();

const balance = await api.query.System.Account.getValue(convertH160ToSS58(contract.target.toString()))
console.log(" == balance is ", balance.data.free)

const deployedContract = new ethers.Contract(
contract.target.toString(),
abi,
wallet1,
);

const tx = await deployedContract.stakeLimit(
hotkey.publicKey,
netuid,
tao(2000),
tao(1000),
true,
);
await tx.wait();

});
});
2 changes: 1 addition & 1 deletion runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// `spec_version`, and `authoring_version` are the same between Wasm and native.
// This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use
// the compatible custom types.
spec_version: 285,
spec_version: 286,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
Expand Down
Loading